extensible-effects 2.6.1.0 → 2.6.1.1
raw patch · 6 files changed
+265/−36 lines, 6 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Control.Eff.State.Lazy: evalState :: s -> Eff (State s : r) w -> Eff r w
+ Control.Eff.State.Lazy: evalState :: s -> Eff (State s : r) a -> Eff r a
- Control.Eff.State.Lazy: execState :: s -> Eff (State s : r) w -> Eff r s
+ Control.Eff.State.Lazy: execState :: s -> Eff (State s : r) a -> Eff r s
- Control.Eff.State.Lazy: runState :: s -> Eff (State s : r) w -> Eff r (w, s)
+ Control.Eff.State.Lazy: runState :: s -> Eff (State s : r) a -> Eff r (a, s)
- Control.Eff.State.Lazy: runState' :: s -> Eff (State s : r) w -> Eff r (w, s)
+ Control.Eff.State.Lazy: runState' :: s -> Eff (State s : r) a -> Eff r (a, s)
- Control.Eff.State.Lazy: runStateR :: s -> Eff (Writer s : (Reader s : r)) w -> Eff r (w, s)
+ Control.Eff.State.Lazy: runStateR :: s -> Eff (Writer s : (Reader s : r)) a -> Eff r (a, s)
- Control.Eff.State.Lazy: transactionState :: forall s r w. Member (State s) r => TxState s -> Eff r w -> Eff r w
+ Control.Eff.State.Lazy: transactionState :: forall s r a. Member (State s) r => TxState s -> Eff r a -> Eff r a
- Control.Eff.State.Strict: evalState :: s -> Eff (State s : r) w -> Eff r w
+ Control.Eff.State.Strict: evalState :: s -> Eff (State s : r) a -> Eff r a
- Control.Eff.State.Strict: execState :: s -> Eff (State s : r) w -> Eff r s
+ Control.Eff.State.Strict: execState :: s -> Eff (State s : r) a -> Eff r s
- Control.Eff.State.Strict: runState :: s -> Eff (State s : r) w -> Eff r (w, s)
+ Control.Eff.State.Strict: runState :: s -> Eff (State s : r) a -> Eff r (a, s)
- Control.Eff.State.Strict: runState' :: s -> Eff (State s : r) w -> Eff r (w, s)
+ Control.Eff.State.Strict: runState' :: s -> Eff (State s : r) a -> Eff r (a, s)
- Control.Eff.State.Strict: runStateR :: s -> Eff (Writer s : (Reader s : r)) w -> Eff r (w, s)
+ Control.Eff.State.Strict: runStateR :: s -> Eff (Writer s : (Reader s : r)) a -> Eff r (a, s)
- Control.Eff.State.Strict: transactionState :: forall s r w. Member (State s) r => TxState s -> Eff r w -> Eff r w
+ Control.Eff.State.Strict: transactionState :: forall s r a. Member (State s) r => TxState s -> Eff r a -> Eff r a
Files
- README.md +238/−8
- extensible-effects.cabal +1/−3
- src/Control/Eff/Internal.hs +0/−1
- src/Control/Eff/State/Lazy.hs +14/−12
- src/Control/Eff/State/Strict.hs +10/−10
- src/Data/OpenUnion.hs +2/−2
README.md view
@@ -1,18 +1,248 @@-extensible-effects is based on the work-[Extensible Effects: An Alternative to Monad Transformers](http://okmij.org/ftp/Haskell/extensible/).-Please read the [paper](http://okmij.org/ftp/Haskell/extensible/exteff.pdf) and-the followup [freer paper](http://okmij.org/ftp/Haskell/extensible/more.pdf) for-details. Additional explanation behind the approach can be found on [Oleg's website](http://okmij.org/ftp/Haskell/extensible/). +# Extensible effects+ [](https://travis-ci.org/suhailshergill/extensible-effects) [](https://gitter.im/suhailshergill/extensible-effects?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](http://waffle.io/suhailshergill/extensible-effects) [](http://waffle.io/suhailshergill/extensible-effects) -## Advantages+*Implement effectful computations in a modular way!* - * Effects can be added, removed, and interwoven without changes to code not- dealing with those effects.+The main and only monad is built upon `Eff` from `Control.Eff`.+`Eff r a` is parameterized by the effect-list `r` and the monadic-result type+`a` similar to other monads.+It is the intention that all other monadic computations can be replaced by the+use of `Eff`.++In case you know monad transformers or `mtl`:+This library provides only one monad that includes all your effects instead of+layering different transformers.+It is not necessary to lift the computations through a monad stack.+Also, it is not required to lift every `Monad*` typeclass (like `MonadError`)+though all transformers.++## Quickstart++To experiment with this library, it is suggested to write some lines within+`ghci`.+This section will include some code-examples, which you should try on your own!++Recommended Procedure:++1. add `extensible-effects` as a dependency to a existing cabal or stack project+or `git clone https://github.com/suhailshergill/extensible-effects.git`+2. start `stack ghci` or `cabal repl`+3. import some library modules as described in this section++*examples are a work in progress and there will be some Quickstart module to go+along the guide here*++*examples...*++## Tour through Extensible Effects++This section explains the basic concepts of this library.++### The Effect List++```haskell+import Control.Eff+```++The effect list `r` in the type `Eff r a` is a central concept in this library.+It is a type-level list containing effect types.++If `r` is the empty list, then the computation `Eff r` (or `Eff '[]`) does not+contain any effects to be handled and therefore is a pure computation.+In this case, the result value can be retrieved by `run :: Eff '[] a -> a`++For programming within the `Eff r` monad, it is almost never necessary to list+all effects that can appear.+It suffices to state what types of effects are at least required.+This is done via the `Member t r` typeclass. It describes that the type `t`+occurs inside the list `r`.+If you really want, you can still list all Effects and their order in which+they are used (e.g. `Eff '[Reader r, State s] a`).++### Handling Effects++Functions containing something like `Eff (x ': r) a -> Eff r a` handle effects.++The transition from the longer list of effects `(x ': r)` to just `r`+is a type-level indicator that the effect `x` has been handled.+Depending on the effect, some additional input might be required or some+different output than just `a` is produced.++The handler functions typically are called `run*`, `eval*` or `exec*`.++### Most common Effects++The most common effects used are `Writer`, `Reader`, `Exception` and `State`.++For the `Writer`, `Reader` and `State`, there are lazy and a strict variants.+Each has its own module that provide the same interface.+By importing one or the other, it can be controlled if the effect is strict or+lazy in its inputs and outputs.+Note that this changes the strictness of that effect only.++In this section, only the core functions associated with an effect are+presented.+Have a look at the modules for additional details.++#### The Exception Effect++```haskell+import Control.Eff.Exception+```++The exception effect adds the possibility to exit a computation preemptively+with an exception.+Note that the exceptions from this library are handled by the programmer and+have nothing to do with exceptions thrown inside the Haskell run-time.++```haskell+throwError :: Member (Exc e) r => e -> Eff r a+runError :: Eff (Exc e ': r) a -> Eff r (Either e a)+```++An exception can be thrown using the `throwError` function.+Its return type is `Eff r a` with an arbitrary type `a`.+When handling the effect, the result-type changes to `Either e a` instead of+just `a`.+This indicates how the effect is handled: The returned value is either the+thrown exception or the value returned from a successful computation.++#### The State Effect++```haskell+import Control.Eff.State.{Lazy | Strict}+```++The state effect provides readable and writable state during a computation.++```haskell+get :: Member (State s) r => Eff r s+put :: Member (State s) r => s -> Eff r ()+modify :: Member (State s) r => (s -> s) -> Eff r ()+runState :: s -> Eff (State s ': r) a -> Eff r (a, s)+```++The `get` functions accesses the current state and makes it usable within the+further computation.+The `put` function sets the state to the given value.+`modify` updates the state using a mapping function by combining `get` and+`put`.++The state-effect is handled using the `runState` function.+It takes the initial state as an argument and returns the final state and+effect-result.++#### The Reader Effect++```haskell+import Control.Eff.Reader.{Strict | Lazy}+```++The reader effect provides an environment that can be read.+Sometimes it is considered as read-only state.++```haskell+ask :: Member (Reader e) r => e -> Eff r e+runReader :: e -> Eff (Reader e ': r) a -> Eff r a+```++The environment given to the handle the reader effect is the one given during+the computation if asked for.++#### The Writer Effect++```haskell+import Control.Eff.Writer.{Strict | Lazy}+```++The writer effect allows to output messages during a computation.+It is sometimes referred to as write-only state, which gets retrieved at the+end of the computation.++```haskell+tell :: Member (Writer w) r => w -> Eff r ()+runWriter :: (w -> b -> b) -> b -> Eff (Writer w ': r) a -> Eff r (a, b)+runListWriter :: Eff (Writer w ': r) a -> Eff r (a, [w])+```++Running a writer can be done in several ways.+The most general function is `runWriter` that folds over all written values.+However, if you only want to collect the the values written, the `runListWriter`+function does that.++Note that compared to mtl, the value written has no Monoid constraint on it and+can be collected in any way.++### Using multiple Effects++The main benefit of this library is that multiple effects can be included+with ease.++If you need state and want to be able exit the computation with an exception,+the type of your effectful computation would be the one of `myComp` below.+Then, both the state and exception effect-functions can be used.+To handle the effects, both the `runState` and `runError` functions have to be+provided.++```haskell+myComp :: (Member (Exc e) r, Member (State s) r) => Eff r a++run1 :: (Either e a, s)+run1 = run . runState initalState . runError $ myComp++run2 :: Either e (a, s)+runs = run . runError . runState initalState $ myComp+```++However, the order of the handlers does matter for the final result.+`run1` and `run2` show different executions of the same effectful computation.+In `run1`, the returned state `s` is the last state seen before an eventual+exception gets thrown (similar to the semantics in typical imperative+languages), while in `run2` the final state is returned only if the whole+computation succeeded - transaction style.++### Tips and tricks++There are several constructs that make it easier to work with the effects.++If only a part of the result is necessary for the further computation, have a+look at the `eval*` and `exec*` functions, which exist for some effects.+The `exec*` functions discard the result of the computation (the `a` type).+The `eval*` functions discard the final result of the effect.++Instead of writing+`(Member (Exc e) r, Member (State s) r) => ...` it is+possible to use the type operator `<::` and write+`[ Exc e, State s ] <:: r => ...`, which has the same meaning.++## Other Effects++*work in progress*++## Integration with IO++*work in progress*++## Integration with Monad Transformers++*work in progress*++## Writing your own Effects and Handlers++*work in progress*++## Background++extensible-effects is based on the work+[Extensible Effects: An Alternative to Monad Transformers](http://okmij.org/ftp/Haskell/extensible/).+The [paper](http://okmij.org/ftp/Haskell/extensible/exteff.pdf) and+the followup [freer paper](http://okmij.org/ftp/Haskell/extensible/more.pdf)+contain details. Additional explanation behind the approach can be found on [Oleg's website](http://okmij.org/ftp/Haskell/extensible/). ## Limitations
extensible-effects.cabal view
@@ -6,7 +6,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 2.6.1.0+version: 2.6.1.1 -- A short (one-line) description of the package. synopsis: An Alternative to Monad Transformers@@ -15,8 +15,6 @@ description: This package introduces datatypes for typeclass-constrained effects, as an alternative to monad-transformer based (datatype-constrained) approach of multi-layered effects.- For more information, see the original paper at- <http://okmij.org/ftp/Haskell/extensible/exteff.pdf>. Any help is appreciated!
src/Control/Eff/Internal.hs view
@@ -262,4 +262,3 @@ runLift (E u q) = case prj u of Just (Lift m) -> m >>= runLift . qApp q Nothing -> error "Impossible: Nothing cannot occur"-
src/Control/Eff/State/Lazy.hs view
@@ -76,18 +76,20 @@ -- inline get/put, even if I put the INLINE directives and play with phases. -- (Inlining works if I use 'inline' explicitly). -runState' :: s -> Eff (State s ': r) w -> Eff r (w,s)+-- | Run a state effect. compared to the @runState@ function, this is+-- implemented naively and is expected to perform slower.+runState' :: s -> Eff (State s ': r) a -> Eff r (a, s) runState' s = handle_relay_s s (\s0 x -> return (x,s0)) (\s0 sreq k -> case sreq of Get -> k s0 s0 Put s1 -> k s1 ()) --- Since State is so frequently used, we optimize it a bit--- | Run a State effect+-- | Run a State effect. This variant is a bit optimized compared to+-- @runState'@. runState :: s -- ^ Initial state- -> Eff (State s ': r) w -- ^ Effect incorporating State- -> Eff r (w,s) -- ^ Effect containing final state and a return value+ -> Eff (State s ': r) a -- ^ Effect incorporating State+ -> Eff r (a, s) -- ^ Effect containing final state and a return value runState s (Val x) = return (x,s) runState s (E u q) = case decomp u of Right Get -> runState s (q ^$ s)@@ -99,22 +101,22 @@ modify f = get >>= put . f -- | Run a State effect, discarding the final state.-evalState :: s -> Eff (State s ': r) w -> Eff r w+evalState :: s -> Eff (State s ': r) a -> Eff r a evalState s = fmap fst . runState s -- | Run a State effect and return the final state.-execState :: s -> Eff (State s ': r) w -> Eff r s+execState :: s -> Eff (State s ': r) a -> Eff r s execState s = fmap snd . runState s -- | An encapsulated State handler, for transactional semantics -- The global state is updated only if the transactionState finished -- successfully data TxState s = TxState-transactionState :: forall s r w. Member (State s) r =>- TxState s -> Eff r w -> Eff r w+transactionState :: forall s r a. Member (State s) r =>+ TxState s -> Eff r a -> Eff r a transactionState _ m = do s <- get; loop s m where- loop :: s -> Eff r w -> Eff r w+ loop :: s -> Eff r a -> Eff r a loop s (Val x) = put s >> return x loop s (E (u::Union r b) q) = case prj u :: Maybe (State s b) of Just Get -> loop s (q ^$ s)@@ -124,10 +126,10 @@ -- | A different representation of State: decomposing State into mutation -- (Writer) and Reading. We don't define any new effects: we just handle the -- existing ones. Thus we define a handler for two effects together.-runStateR :: s -> Eff (Writer s ': Reader s ': r) w -> Eff r (w,s)+runStateR :: s -> Eff (Writer s ': Reader s ': r) a -> Eff r (a, s) runStateR s m = loop s m where- loop :: s -> Eff (Writer s ': Reader s ': r) w -> Eff r (w,s)+ loop :: s -> Eff (Writer s ': Reader s ': r) a -> Eff r (a, s) loop s0 (Val x) = return (x,s0) loop s0 (E u q) = case decomp u of Right (Tell w) -> k w ()
src/Control/Eff/State/Strict.hs view
@@ -78,7 +78,7 @@ -- inline get/put, even if I put the INLINE directives and play with phases. -- (Inlining works if I use 'inline' explicitly). -runState' :: s -> Eff (State s ': r) w -> Eff r (w,s)+runState' :: s -> Eff (State s ': r) a -> Eff r (a, s) runState' !s = handle_relay_s s (\s0 x -> return (x,s0)) (\s0 sreq k -> case sreq of@@ -88,8 +88,8 @@ -- Since State is so frequently used, we optimize it a bit -- | Run a State effect runState :: s -- ^ Effect incorporating State- -> Eff (State s ': r) w -- ^ Initial state- -> Eff r (w,s) -- ^ Effect containing final state and a return value+ -> Eff (State s ': r) a -- ^ Initial state+ -> Eff r (a, s) -- ^ Effect containing final state and a return value runState !s (Val x) = return (x,s) runState !s (E u q) = case decomp u of Right Get -> runState s (q ^$ s)@@ -101,12 +101,12 @@ modify f = get >>= put . f -- | Run a State effect, discarding the final state.-evalState :: s -> Eff (State s ': r) w -> Eff r w+evalState :: s -> Eff (State s ': r) a -> Eff r a evalState !s = fmap fst . runState s {-# INLINE evalState #-} -- | Run a State effect and return the final state.-execState :: s -> Eff (State s ': r) w -> Eff r s+execState :: s -> Eff (State s ': r) a -> Eff r s execState !s = fmap snd . runState s {-# INLINE execState #-} @@ -114,11 +114,11 @@ -- The global state is updated only if the transactionState finished -- successfully data TxState s = TxState-transactionState :: forall s r w. Member (State s) r =>- TxState s -> Eff r w -> Eff r w+transactionState :: forall s r a. Member (State s) r =>+ TxState s -> Eff r a -> Eff r a transactionState _ m = do s <- get; loop s m where- loop :: s -> Eff r w -> Eff r w+ loop :: s -> Eff r a -> Eff r a loop s (Val x) = put s >> return x loop s (E (u::Union r b) q) = case prj u :: Maybe (State s b) of Just Get -> loop s (q ^$ s)@@ -128,10 +128,10 @@ -- | A different representation of State: decomposing State into mutation -- (Writer) and Reading. We don't define any new effects: we just handle the -- existing ones. Thus we define a handler for two effects together.-runStateR :: s -> Eff (Writer s ': Reader s ': r) w -> Eff r (w,s)+runStateR :: s -> Eff (Writer s ': Reader s ': r) a -> Eff r (a, s) runStateR !s m = loop s m where- loop :: s -> Eff (Writer s ': Reader s ': r) w -> Eff r (w,s)+ loop :: s -> Eff (Writer s ': Reader s ': r) a -> Eff r (a, s) loop s0 (Val x) = return (x,s0) loop s0 (E u q) = case decomp u of Right (Tell w) -> k w ()
src/Data/OpenUnion.hs view
@@ -115,7 +115,7 @@ #else -- | Explicit type-level equality condition is a dirty -- hack to eliminate the type annotation in the trivial case,--- such as @run (runReader get ())@.+-- such as @run (runReader () get)@. -- -- There is no ambiguity when finding instances for -- @Member t (a ': b ': r)@, which the second instance is selected.@@ -142,7 +142,7 @@ -- | A useful operator for reducing boilerplate. -- -- @--- f :: [Reader Int, Writer String] ::> r+-- f :: [Reader Int, Writer String] <:: r -- => a -> Eff r b -- @ -- is equal to