Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/qzchenwl/more-extensible-effects
An implementation of "Freer Monads, More Extensible Effects".
https://github.com/qzchenwl/more-extensible-effects
extensible-effects free-monad haskell
Last synced: about 2 months ago
JSON representation
An implementation of "Freer Monads, More Extensible Effects".
- Host: GitHub
- URL: https://github.com/qzchenwl/more-extensible-effects
- Owner: qzchenwl
- License: bsd-3-clause
- Created: 2017-01-15T03:49:53.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2017-01-29T12:01:27.000Z (almost 8 years ago)
- Last Synced: 2024-04-25T21:03:34.126Z (9 months ago)
- Topics: extensible-effects, free-monad, haskell
- Language: Haskell
- Size: 38.1 KB
- Stars: 7
- Watchers: 3
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# More Extensible Effects
This package is an implementation of ["Freer Monads, More Extensible Effects"](http://okmij.org/ftp/Haskell/extensible/more.pdf).
Much of the implementation is a repackaging and cleaning up of the reference materials provided [here](http://okmij.org/ftp/Haskell/extensible/):
- [Eff1.hs](http://okmij.org/ftp/Haskell/extensible/Eff1.hs)
- [OpenUnion5.hs](http://okmij.org/ftp/Haskell/extensible/OpenUnion5.hs)
- [FTCQueue1.hs](http://okmij.org/ftp/Haskell/extensible/FTCQueue1.hs)## Overview
- Control
- Monad
- [Eff](src/Control/Monad/Eff.hs)
- [Examples](src/Control/Monad/Eff/Examples.hs)
- [Teletype](src/Control/Monad/Eff/Examples/Teletype.hs)
- [VerboseAddition](src/Control/Monad/Eff/Examples/VerboseAddition.hs)
- [Exception](src/Control/Monad/Eff/Exception.hs)
- [Internal](src/Control/Monad/Eff/Internal.hs)
- [Lift](src/Control/Monad/Eff/Lift.hs)
- [NdetEff](src/Control/Monad/Eff/NdetEff.hs)
- [Reader](src/Control/Monad/Eff/Reader.hs)
- [State](src/Control/Monad/Eff/State.hs)
- [StateRW](src/Control/Monad/Eff/StateRW.hs)
- [Writer](src/Control/Monad/Eff/Writer.hs)
- Data
- [FTCQueue](src/Data/FTCQueue.hs)
- [OpenUnion](src/Data/OpenUnion.hs)## Examples
### Log Effect ([24 Days of Hackage: extensible-effects](https://ocharles.org.uk/blog/posts/2013-12-04-24-days-of-hackage-extensible-effects.html))
[VerboseAddition.hs](src/Control/Monad/Eff/Examples/VerboseAddition.hs)
```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}module Control.Monad.Eff.Examples.VerboseAddition where
import Control.Monad.Eff
import Control.Monad.Eff.Lift
import Prelude hiding (log)data Log v where
Log :: String -> Log ()log :: Member Log r => String -> Eff r ()
log = send . LogrunLogger :: Eff (Log ': r) a -> Eff r (a, [String])
runLogger = handleRelay ret handle
where
ret :: a -> Eff r (a, [String])
ret x = return (x, [])
handle :: Handler Log r (a, [String])
handle (Log s) k = do
(x, ss) <- k ()
return (x, s:ss)runIOLogger :: forall r a. MemberU2 Lift (Lift IO) r => Eff (Log ': r) a -> Eff r a
runIOLogger = handleRelay ret handle
where
ret :: a -> Eff r a
ret = return
handle :: Handler Log r a
handle (Log s) k = lift (putStrLn s) >>= kexample :: Member Log r => Eff r Int
example = do
log "I'm starting with 1..."
let x = 1log "and I'm adding 2..."
let y = 2let r = x + y
log $ "Looks like the result is " ++ show r
return r
```
Now we can run the program in pure or impure way:```
λ> run (runLogger verboseAddition)
(3,["I'm starting with 1...","and I'm adding 2...","Looks like the result is 3"])
λ> runLift (runIOLogger verboseAddition)
I'm starting with 1...
and I'm adding 2...
Looks like the result is 3
3
```### Teletype ([Purify code using free monads](http://www.haskellforall.com/2012/07/purify-code-using-free-monads.html))
[Teletype.hs](src/Control/Monad/Eff/Examples/Teletype.hs)
```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}module Control.Monad.Eff.Examples.Teletype where
import Control.Monad.Eff
import Control.Monad.Eff.Lift
import System.Exit (exitSuccess)data Teletype x where
GetLine :: Teletype String
PutStrLn :: String -> Teletype ()
ExitSuccess :: Teletype ()putStrLn' :: Member Teletype r => String -> Eff r ()
putStrLn' = send . PutStrLngetLine' :: Member Teletype r => Eff r String
getLine' = send GetLineexitSuccess' :: Member Teletype r => Eff r ()
exitSuccess' = send ExitSuccessrunTeletype :: [String] -> Eff (Teletype ': r) a -> Eff r [String]
runTeletype ss = handleRelayS ss ret handle
where
ret :: [String] -> a -> Eff r [String]
ret _ a = return []handle :: HandlerS [String] Teletype r [String]
handle (s:stdin) GetLine k = k stdin s
handle _ GetLine k = error "Insufficient input"
handle stdin (PutStrLn s) k = do
stdout <- k stdin ()
return (s:stdout)
handle _ ExitSuccess k = return []runIOTeletype :: forall r a. MemberU2 Lift (Lift IO) r => Eff (Teletype ': r) a -> Eff r a
runIOTeletype = handleRelay ret handle
where
ret :: a -> Eff r a
ret = returnhandle :: Handler Teletype r a
handle GetLine k = lift getLine >>= k
handle (PutStrLn s) k = lift (putStrLn s) >>= k
handle ExitSuccess k = lift exitSuccess >>= kexample :: Member Teletype r => Eff r ()
example = do
str <- getLine'
putStrLn' ("put: " ++ str)
str <- getLine'
putStrLn' ("put: " ++ str)
exitSuccess'
putStrLn' "should not appear"
```Run it purely:
```
λ> run $ runTeletype ["hello", "world", "and more"] example
["put: hello","put: world"]λ> run $ runTeletype ["hello"] example
*** Exception: Insufficient input
CallStack (from HasCallStack):
error, called at /work/src/Control/Monad/Eff/Examples/Teletype.hs:35:39 in main:Control.Monad.Eff.Examples.Teletype
```Run it in IO:
```
λ> runLift $ runIOTeletype example
hello
put: hello
world
put: world
*** Exception: ExitSuccess
```## Usage Tips
### Effect Intepreter
The most complex part of new effect definition is the 'runX' function. As you can see in the above examples, it's usually defined by `handleRelay ret handle` with your customized `ret` to return value and `handle` to handle continuation.
It's similar to what you do to implement an instance of Monad (`ret` for `return`, `handle` for `>>=`). You can read `handleRelay ret handle` as run this monad with instance defined by `ret` and `handle`.