lexi-lambda / eff

🚧 a work in progress effect system for Haskell 🚧
ISC License
124 stars 2 forks source link

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 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: