• Stars
    star
    113
  • Rank 309,338 (Top 7 %)
  • Language
    Haskell
  • License
    ISC License
  • Created over 2 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🚧 a work in progress effect system for Haskell 🚧

eff β€” screaming fast extensible effects for less Build Status Documentation

🚧 This library is currently under construction. 🚧

eff is a work-in-progress implementation of an extensible effect system for Haskell, a general-purpose solution for tracking effects at the type level and handling them in flexible ways. Compared to other effect systems currently available, eff differentiates itself in the following respects:

  • eff is really fast. Built on top of low-level primitives added to the GHC RTS to support capturing slices of the call stack, eff is performant by design. Using a direct implementation of delimited control allows it to be fast without relying on fickle compiler optimizations to eliminate indirection.

    Traditional effect system microbenchmarks fail to capture the performance of real code, as they are so small that GHC often ends up inlining everything. In real programs, GHC compiles most effect-polymorphic code via dictionary passing, not specialization, causing the performance of other effect systems to degrade beyond what microbenchmarks would imply. eff takes care to allow GHC to generate efficient code without the need for whole-program specialization.

  • eff is low-boilerplate and easy to use, even without Template Haskell or any generic programming. eff’s interface is comparable to freer-simple and polysemy, but writing new effect handlers is made even simpler thanks to a small set of highly expressive core operations.

  • eff is expressive, providing support for both first-order/algebraic effects and higher-order/scoped effects, like fused-effects and polysemy (but unlike freer-simple).

  • eff’s semantics is precise and easy to reason about, based on models of delimited control. Other approaches to scoped operations (including those taken in mtl, fused-effects, and polysemy) have behavior that changes depending on handler order, and some combinations can lead to nonsensical results. eff’s semantics is consistent regardless of handler order, and scoped operations compose in predictable ways.

eff in action

To illustrate just how easy it is to define and handle effects in eff, the following code example includes 100% of the code necessary to define a custom FileSystem effect and two handlers, one that runs in IO and another that uses an in-memory virtual file system:

import qualified System.IO as IO
import Prelude hiding (readFile, writeFile)
import Control.Effect

-- -----------------------------------------------------------------------------
-- effect definition

data FileSystem :: Effect where
  ReadFile :: FilePath -> FileSystem m String
  WriteFile :: FilePath -> String -> FileSystem m ()

readFile :: FileSystem :< effs => FilePath -> Eff effs String
readFile = send . ReadFile

writeFile :: FileSystem :< effs => FilePath -> String -> Eff effs ()
writeFile a b = send $ WriteFile a b

-- -----------------------------------------------------------------------------
-- IO handler

runFileSystemIO :: IOE :< effs => Eff (FileSystem ': effs) a -> Eff effs a
runFileSystemIO = interpret \case
  ReadFile path -> liftIO $ IO.readFile path
  WriteFile path contents -> liftIO $ IO.writeFile path contents

-- -----------------------------------------------------------------------------
-- pure handler

runFileSystemPure :: Error String :< effs => Eff (FileSystem ': effs) a -> Eff effs a
runFileSystemPure = lift
  >>> interpret \case
        ReadFile path -> do
          fileSystem <- get
          case lookup path fileSystem of
            Just contents -> pure contents
            Nothing       -> throw ("readFile: no such file " <> path)
        WriteFile path contents -> do
          fileSystem <- get
          -- add the new file and remove an old file with the same name, if it exists
          put ((path, contents) : filter ((/= path) . fst) fileSystem)
  >>> evalState @[(FilePath, String)] []

That’s it. For a thorough explanation of how the above example works, see the eff documentation.

Implementation status

eff is a work in progress, and since it requires changes to the GHC RTS, you cannot use it yet on any released version of GHC. If there is interest, I can try to provide builds of GHC with the necessary changes to use eff, but otherwise you will need to wait for them to be merged into GHC proper before using eff yourself. There is currently an open GHC proposal to add the necessary operations, and the work-in-progress implementation branch is available here.

Looking beyond that, many things are still not yet implemented. More work needs to be done to properly interoperate with IO exceptions, and the set of built-in effects currently provided is very small. However, all the existing functionality works, and it has been designed to support extensions, so I do not anticipate any difficulty supporting them.

This library is also sorely lacking a benchmark suite. I have a small set of microbenchmarks I have been using to test out various scenarios and edge cases of different effect libraries, but they need to be cleaned up and added to this repository, and a set of less synthetic benchmarks are also important to assess real-world performance. If you have a small but non-trivial program where differences in effect system performance are significant, I would be much obliged if you could share it to build a more realistic set of effect system benchmarks.

Acknowledgements, citations, and related work

All code in eff is original in the sense that it was not taken directly from other libraries, but much of it is directly inspired by the existing work of many others. The following is a non-exhaustive list of people and works that have had a significant impact, directly or indirectly, on eff’s design and implementation:

More Repositories

1

hackett

WIP implementation of a Haskell-like Lisp in Racket
Racket
1,149
star
2

freer-simple

A friendly effect system for Haskell
Haskell
222
star
3

mtl-style-example

A small example of using mtl style to unit test effectful code
Haskell
106
star
4

higher-rank

A small Haskell implementation of Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism
Haskell
101
star
5

racket-r7rs

An implementation of R7RS in Racket
Racket
92
star
6

megaparsack

Racket parser combinators inspired by parsack and megaparsec
Racket
76
star
7

racket-collections

Generic collections API for Racket
Racket
52
star
8

blackboard

A (very) unfinished OpenType Math layout and rendering engine in Racket
Racket
51
star
9

threading

Threading macros for Racket
Racket
47
star
10

talks

Racket
42
star
11

functional

Functional interfaces and datatypes for Racket
Racket
42
star
12

monad-validate

A Haskell monad transformer library for data validation
Haskell
35
star
13

shattered-plans

Java
31
star
14

racket-commonmark

Fast, CommonMark-compliant Markdown parser written in Racket
Racket
31
star
15

heroku-buildpack-racket

a Heroku buildpack for deploying apps written in Racket
Shell
27
star
16

lexi-lambda.github.io

Racket
27
star
17

racket-mvar

An implementation of Haskell’s MVars in Racket
Racket
27
star
18

mini-ml

An experimental implementation of a tiny ML-like language using an embedded custom expander (WIP, currently just System F core language)
Racket
21
star
19

racket-higher-rank

Racket
21
star
20

envy

An environment variable manager for Racket applications
Racket
17
star
21

racket-curly-fn

A meta-language for adding Clojure-style shorthand function literals to arbitrary Racket languages.
Racket
16
star
22

racket-tulip

Racket
15
star
23

racket-alexis-collections

Deprecated. Use https://github.com/lexi-lambda/racket-collections instead.
Racket
12
star
24

libsol

A C runtime for the sol programming language.
C
11
star
25

racket-multimethod

Proof-of-concept, mostly safe multimethods in Racket
Racket
11
star
26

case-kw-lambda

Racket
11
star
27

racket-alexis-pvector

Deprecated. Use https://github.com/lexi-lambda/racket-pvector instead.
Racket
11
star
28

scripty

Distributable shell scripts with dependencies
Racket
10
star
29

racket-macro-exercises

Small macro challenges to help learn Racket’s macro system
Racket
9
star
30

lens-examples

A handful of small examples of parts of the Haskell `lens` package
Haskell
8
star
31

racket-2htdp-typed

A port of 2htdp to Typed Racket
Racket
8
star
32

learning-haskell

Racket
8
star
33

racket-pvector

Fast, immutable, persistent vectors for Racket
Racket
8
star
34

dotfiles

Racket
7
star
35

namespaced-transformer

Racket
6
star
36

racket-sample-heroku-app

a sample Racket application for deployment to Heroku
Racket
6
star
37

factorio-blueprints

Makefile
6
star
38

syntax-classes

Racket
5
star
39

SimpleJail

Simplistic Jail plugin for Bukkit
Java
5
star
40

clojure-fuzzy-urls

Clojure
5
star
41

struct-update

Racket
5
star
42

th-to-exp

Deprecated. Use Language.Haskell.TH.Syntax.Lift instead.
Haskell
4
star
43

aws-cloudformation-template

Racket
4
star
44

litpub

A small Medium clone in Racket designed for publishing stories
Racket
4
star
45

decontaminate

A Ruby DSL for extracting data from complicated XML documents
Ruby
3
star
46

alexis-collection-lens

Lens provider for Racket generic collections
Racket
3
star
47

SNESTile

A cross-platform SNES graphics editor.
Java
3
star
48

collections-lens

Lenses for Racket generic collections
Racket
3
star
49

racket-irc-client

Racket
3
star
50

racket-alexis

Various personal libraries and utilities for Racket
Racket
3
star
51

racket-match-plus

Racket
2
star
52

rackunit-spec

Racket
2
star
53

simple_split

dead simple A/B testing on Rails
Ruby
2
star
54

haskell-rolodex

Haskell
2
star
55

type-assertions

Runtime type assertions for testing
Haskell
1
star
56

monads-lecture

Haskell
1
star
57

racket-vector-struct

Racket
1
star
58

racket-semver

A Racket implementation of the semver specification
Racket
1
star
59

imgcap

Racket
1
star
60

incremental

Haskell
1
star
61

ut2k4-team-stats

A simple app that uses the data from the UT2k4 Stats server to track the performance of team duos
Racket
1
star
62

template-dom

A library for creating (V)DOM nodes using ES2015 tagged template strings
JavaScript
1
star