Home

Awesome

cleff - fast and concise extensible effects

GitHub Workflow Status Hackage

cleff is an extensible effects library for Haskell, with a focus on the balance of performance, expressiveness and ease of use. It provides a set of predefined effects that you can conveniently reuse in your program, as well as low-boilerplate mechanisms for defining and interpreting new domain-specific effects on your own.

In essence, cleff offers:

Example

This is the code that defines the classic Teletype effect. It only takes 20 lines to define the effect and two interpretations, one using stdio and another reading from and writing to a list:

import Cleff
import Cleff.Input
import Cleff.Output
import Cleff.State
import Data.Maybe (fromMaybe)

-- Effect definition
data Teletype :: Effect where
  ReadTTY :: Teletype m String
  WriteTTY :: String -> Teletype m ()
makeEffect ''Teletype

-- Effect Interpretation via IO
runTeletypeIO :: IOE :> es => Eff (Teletype : es) a -> Eff es a
runTeletypeIO = interpretIO \case
  ReadTTY    -> getLine
  WriteTTY s -> putStrLn s

-- Effect interpretation via other pure effects
runTeletypePure :: [String] -> Eff (Teletype : es) w -> Eff es [String]
runTeletypePure tty = fmap (reverse . snd)
  . runState [] . outputToListState
  . runState tty . inputToListState
  . reinterpret2 \case
    ReadTTY -> fromMaybe "" <$> input
    WriteTTY msg -> output msg

-- Using the effect

echo :: Teletype :> es => Eff es ()
echo = do
  x <- readTTY
  if null x then pure ()
    else writeTTY x >> echo

echoPure :: [String] -> [String]
echoPure input = runPure $ runTeletypePure input echo

main :: IO ()
main = runIOE $ runTeletypeIO echo

See example/ for more examples.

Benchmarks

These are the results of effectful's microbenchmarks, compiled by GHC 8.10.7. Each diagram shows the average run time of each effect library's implementation of an identical program; lower is better. Each benchmark suite has two flavors - shallow and deep - where the shallow variant only uses necessary effects, and the deep variant adds 10 redundant Reader effects, to simulate more realistic scenarios. Keep in mind that these are very short and synthetic programs, and may or may not tell the accurate performance characteristics of different effect libraries in real use.

Differences from effectful

If you know about effectful, you may notice that cleff and effectful seem to make many similar claims and have a similar underlying implementation. In microbenchmarks, cleff is slightly behind effectful. This may make you confused about the differences between the two libraries. To put it simply, cleff has a more versatile and expressive effect interpretation mechanism, and a lighter weight API. In contrast, effectful gains its performance advantage by providing static dispatch for some internal effects, which means they cannot have multiple interpretations.

References

These are the useful resources that inspired this library's design and implementation.

Papers:

Libraries:

Talks:

Blog posts: