extensible-effects 1.9.0.1 → 1.9.1.0
raw patch · 5 files changed
+139/−5 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Eff.Operational: Program :: (instr a) -> (a -> v) -> Program instr v
+ Control.Eff.Operational: data Program instr v
+ Control.Eff.Operational: instance Functor (Program instr)
+ Control.Eff.Operational: instance Typeable Program
+ Control.Eff.Operational: runProgram :: Typeable f => (forall x. f x -> Eff r x) -> Eff (Program f :> r) a -> Eff r a
+ Control.Eff.Operational: singleton :: (Typeable instr, Member (Program instr) r) => instr a -> Eff r a
+ Control.Eff.Operational.Example: Print :: String -> Jail ()
+ Control.Eff.Operational.Example: Scan :: Jail String
+ Control.Eff.Operational.Example: adventIO :: (Member (Lift IO) r, SetMember Lift (Lift IO) r) => Jail a -> Eff r a
+ Control.Eff.Operational.Example: adventPure :: (Member (Writer String) r, Member (State [String]) r) => Jail a -> Eff r a
+ Control.Eff.Operational.Example: data Jail a
+ Control.Eff.Operational.Example: instance Typeable Jail
+ Control.Eff.Operational.Example: prog :: Member (Program Jail) r => Eff r ()
Files
- README.md +9/−4
- extensible-effects.cabal +3/−1
- src/Control/Eff/Operational.hs +69/−0
- src/Control/Eff/Operational/Example.hs +43/−0
- test/Test.hs +15/−0
README.md view
@@ -2,15 +2,18 @@ [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) for details. -[](https://travis-ci.org/bfops/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) ## Advantages + * Effects can be added, removed, and interwoven without changes to code not dealing with those effects. ## Disadvantages +### For GHC version 7.8 and upwards * Common functions can't be grouped using typeclasses, e.g. the `ask` and `getState` functions can't be grouped with some @@ -21,9 +24,11 @@ a constraint on `t`, and nothing more. To specify fully, a parameter involving the type `t` would need to be added, which would defeat the point of having the grouping in the first place.- * Requires a `Typeable` instance on the return type. This is no longer a limitation on GHC versions 7.8 and above.+ * fixed by https://github.com/suhailshergill/extensible-effects/issues/38++### For GHC versions prior to 7.8 * Neither `Eff` nor `(:>)` has a `Typeable` instance, and can thus often not- be used as a return type (e.g. `State` type) for other `Eff`s. This is no- longer a concern for GHC versions 7.8 and above.+ be used as a return type (e.g. `State` type) for other `Eff`s. + * fixed by https://github.com/suhailshergill/extensible-effects/issues/38
extensible-effects.cabal view
@@ -6,7 +6,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 1.9.0.1+version: 1.9.1.0 -- A short (one-line) description of the package. synopsis: An Alternative to Monad Transformers@@ -72,6 +72,8 @@ Control.Eff.Writer.Lazy Control.Eff.Writer.Strict Control.Eff.Trace+ Control.Eff.Operational+ Control.Eff.Operational.Example Control.Monad.Free.Reflection Data.OpenUnion
+ src/Control/Eff/Operational.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | Operational Monad (<https://wiki.haskell.org/Operational>) implemented with+-- extensible effects.++module Control.Eff.Operational ( Program (..)+ , singleton+ , runProgram+ -- * Usage+ -- $usage+ ) where++import Data.Typeable+import Control.Eff++#if MIN_VERSION_base(4,7,0)+#define Typeable1 Typeable+#endif++-- | Lift values to an effect.+-- You can think this is a generalization of @Lift@.+data Program instr v = forall a. Program (instr a) (a -> v)+#if MIN_VERSION_base(4,7,0)+ deriving (Typeable) -- starting from ghc-7.8 Typeable can only be derived+#else++instance Typeable1 instr => Typeable1 (Program instr) where+ typeOf1 _ = mkTyConApp (mkTyCon3+ "extensible-effects"+ "Control.Eff.Operational"+ "Program")+ [typeOf1 (undefined :: instr ())]+#endif++instance Functor (Program instr) where+ fmap f (Program instr k) = Program instr (f . k)++-- | Lift a value to a monad.+singleton :: (Typeable1 instr, Member (Program instr) r) => instr a -> Eff r a+singleton instr = send . inj $ (Program instr) id++-- | Convert values using given interpreter to effects.+runProgram :: Typeable1 f => (forall x. f x -> Eff r x) -> Eff (Program f :> r) a -> Eff r a+runProgram advent = loop where+ loop = freeMap+ return+ (\u -> handleRelay u loop (\ (Program instr k) -> advent instr >>= loop . k))+++-- $usage+--+-- See 'Control.Eff.Operational.Example' for an example of defining data using+-- GADTs and implementing interpreters from the data to effects.+--+-- To use the interpreter, see below or consult the tests.+--+-- @+--main :: IO ()+--main = do+-- putStrLn . fst . 'run' . 'runMonoidWriter' . 'evalState' [\"foo\",\"bar\"] $ 'runProgram' adventPure prog+-- 'runLift' $ 'runProgram' adventIO prog+-- @
+ src/Control/Eff/Operational/Example.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE CPP #-}++module Control.Eff.Operational.Example where++import Control.Eff.Operational+import Control.Eff+import Control.Eff.Lift+import Control.Eff.Writer.Lazy+import Control.Eff.State.Lazy+import Data.Typeable++#if MIN_VERSION_base(4,7,0)+#define Typeable1 Typeable+#endif++-- | Define data using GADTs.+data Jail a where+ Print :: String -> Jail ()+ Scan :: Jail String+ deriving (Typeable)++prog :: Member (Program Jail) r => Eff r ()+prog = do+ singleton $ Print "getting input..."+ str <- singleton Scan+ singleton $ Print "ok"+ singleton $ Print ("the input is " ++ str)++-- | Then, implements interpreters from the data to effects.+adventIO :: (Member (Lift IO) r, SetMember Lift (Lift IO) r) => Jail a -> Eff r a+adventIO (Print a) = lift $ putStrLn a+adventIO Scan = lift getLine++adventPure :: (Member (Writer String) r, Member (State [String]) r) => Jail a -> Eff r a+adventPure (Print a) = tell (a ++ "\n")+adventPure Scan = do+ x <- get+ case x of+ [] -> return []+ y:ys -> put ys >> return y
test/Test.hs view
@@ -15,6 +15,8 @@ import Control.Eff import Control.Eff.Exception import Control.Eff.Lift+import Control.Eff.Operational as Op+import Control.Eff.Operational.Example as Op.Eg import Control.Eff.Reader.Lazy as LazyR import Control.Eff.State.Lazy as LazyS import Control.Eff.Writer.Lazy as LazyW@@ -178,6 +180,18 @@ return x #endif +testOperational :: Assertion+testOperational =+ let comp :: (Member (LazyS.State [String]) r+ , Member (LazyW.Writer String) r)+ => Eff r ()+ comp = Op.runProgram Op.Eg.adventPure Op.Eg.prog+ go = fst . run . LazyW.runMonoidWriter . LazyS.evalState ["foo", "bar"] $ comp+ in+ assertEqual+ "Evaluating Operational Monad example"+ "getting input...\nok\nthe input is foo\n" go+ tests = [ testProperty "Documentation example." testDocs , testProperty "Test Writer.Lazy.censor." testCensor@@ -193,4 +207,5 @@ #if __GLASGOW_HASKELL__ >= 708 , testProperty "Test nested Eff." testNestedEff #endif+ , testCase "Test Operational monad." testOperational ]