cleff: Fast and concise extensible effects

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Please see the README on GitHub at https://github.com/re-xyr/cleff#readme


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.1.0.0, 0.2.0.0, 0.2.1.0, 0.3.0.0, 0.3.0.1, 0.3.1.0, 0.3.2.0, 0.3.3.0
Change log CHANGELOG.md
Dependencies atomic-primops (>=0.8 && <0.9), base (>=4.12 && <5), containers (>=0.6 && <0.7), exceptions (>=0.10 && <0.11), microlens (>=0.4.9 && <0.5), monad-control (>=1 && <1.1), primitive (>=0.6 && <0.8), template-haskell (>=2.14 && <3), th-abstraction (>=0.2.11 && <0.5), transformers (>=0.5 && <0.7), transformers-base (>=0.4.5 && <0.5), unliftio (>=0.2.8 && <0.3) [details]
License BSD-3-Clause
Copyright 2021 Xy Ren
Author Xy Ren
Maintainer xy.r@outlook.com
Category Control, Effect, Language
Home page https://github.com/re-xyr/cleff#readme
Bug tracker https://github.com/re-xyr/cleff/issues
Source repo head: git clone https://github.com/re-xyr/cleff
Uploaded by daylily at 2022-01-22T12:41:27Z

Modules

[Index] [Quick Jump]

Flags

Manual Flags

NameDescriptionDefault
dynamic-ioe

Make IOE a real effect. This is only for reference purposes and should not be enabled in production code.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for cleff-0.1.0.0

[back to package description]

cleff

Note: this library is still WIP! At the same time, you can preview it at the package candidate.

cleff is an extensible effects library for Haskell. It provides a set of predefined effects that you can conveniently reuse in your program, as well as mechanisms for defining and interpreting new domain-specific effects on your own.

Rationale

We have a bunch of effect libraries out there, why another? To put it simply: cleff is an attempt of implementing an expressive effect system, with good ergonomics and a unified API, and without sacrificing much performance.

In particular, cleff uses a ReaderT IO as the underlying representation of the Eff monad. With this representation, more optimizations are possible, and thus brings hope for lower performance overhead. The effectful library already uses the approach, and proved it to be true; so we follow this path. Indeed, this means that we lose nondeterminism and continuations in the Eff monad - but after all, most effects libraries has broken nondeterminism support, and you could always wrap another monad transformer with support of nondeterminism (e.g. ListT) over the main Eff monad.

However, cleff is also like polysemy, in the sense that it supports very flexible and user-friendly effect interpretation. This includes support for arbitrary effect lifting and subsumption, as well as interpreting higher-order effects, with arguably even less boilerplate than polysemy.

In terms of performance, cleff outperforms polysemy in microbenchmarks, and is slightly behind effectful. However, note that effectful and cleff have very different design principles. While effectful prioritizes performance (by providing static dispatch), cleff focuses on allowing more expressive higher-order effect interpretation and providing user-friendly interpretation combinators. If you would like minimal performance overhead, please still consider effectful.

In conclusion, cleff is an effect library that tries to find a good balance between simplicity, performance, and expressivity.

Example

The classical Teletype effect:

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

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

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

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

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 the effect-zoo microbenchmarks, compiled by GHC 8.10.7. 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:

References

These are the useful resourses that inspired this library.

Libraries:

Talks:

Blog posts: