simple-effects: A simple effect system that integrates with MTL

[ bsd3, control, effects, library ] [ Propose Tags ]

Some of the things you can do with this package:

Declare and check which side-effects your function uses

The library provides a nice, declarative way of specifying exactly what your monadic function does.

getProductAndWriteToFile :: MonadEffects '[Database, FileSystem] m => ProductId -> FilePath -> m ()

This way you can be sure that your harmlessFunction doesn't do unexpected things behind your back. The compiler makes sure that all the effects are accounted for in the function's type.

Dependency injection

Functions are not tied to any specific implementation of an effect meaning you can swap out different implementations without changing your code. Code like this

myFunction :: MonadEffects '[Time, Logging] m => m ()
myFunction = do
    t <- getCurrentTime
    log (show t)

is effectively the same as

myFunction :: Monad m => m ZonedTime -> (String -> m ()) -> m ()
myFunction getCurrentTime log = do
    t <- getCurrentTime
    log (show t)

but the library does all the parameter passing for you. And just like you'd be able to provide any implementation as getCurrentTime and log parameters you can do the same with simple effects.

    & implement (TimeMethods someCurrentTimeImplementation)
    & implement (LoggingMethods someLoggingImplementation)

Test effectful code

Easily provide dummy implementations of your effects to prevent missle-launching during testing.

myEffectfulFunction :: MonadEffects '[Database, Missiles] m => m Int

main = do
    conn <- connectToDb "connStr"
        & implement (realDatabase conn)
        & implement (MissilesMethods (launchMissles "access codes"))

spec = do
    res <- myEffectfulFunction
        & implement (fakeDb Map.empty)
        & implement (MissilesMethods (print "Totally launching missiles"))
    when (res /= 42) (error "Test failed!")

Avoid the \(n \times k\) instance problem

Any effect you define is automatically liftable through any transformer. Most MonadX instances you'd write would look like func a b c = lift (func a b c), so why would you have to write them yourself? simple-effects does it for you using an overlappable instance.

What about effects that aren't that simple? Each effect can specify a constraint on the transformers that it can be lifted through and a mechanism that does the lifting. So you get all the benefits of automatic lifting of simple effects and retain all of the flexibility of complex ones.

Define custom effects with very little programming overhead

Lets say we need a way to get coordinates for some address. Here's how we'd declare that functionality.

data Geolocation m = GeolocationMethods
    { _getLocation :: Address -> m Coordinates }
    deriving (Generic, Effect)
getLocation :: MonadEffect Geolocation m => Address -> m Coordinates
getLocation = _getLocation effect

That's all you need to start using your effect in functions.

getUsersLocation :: (MonadEffect Geolocation m, MonadIO m) => m Coordinates
getUsersLocation = do
    liftIO $ putStrLn "Please enter your address:"
    addr <- liftIO readLn
    getLocation addr
Check out the tutorial modules for more details
Versions [faq],,,,,,,,,,,,,,,,,,,,,,,,,,,, (info)
Change log
Dependencies array, async, base (>=4.7 && <5), bytestring, exceptions, list-t, monad-control (==1.0.*), MonadRandom, mtl, text, transformers, transformers-base (==0.4.*) [details]
License BSD-3-Clause
Copyright 2018 Luka Horvat
Author Luka Horvat
Revised Revision 1 made by darwin226 at Mon Nov 19 23:36:48 UTC 2018
Category Control
Home page
Uploaded by darwin226 at Mon Nov 19 23:20:24 UTC 2018
Distributions NixOS:
Downloads 4857 total (390 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by rule of succession]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2018-11-19 [all 1 reports]


[Index] [Quick Jump]


Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

For package maintainers and hackage trustees