packages feed

effect-handlers (empty) → 0.1.0.0

raw patch · 15 files changed

+521/−0 lines, 15 filesdep +HUnitdep +QuickCheckdep +basesetup-changed

Dependencies added: HUnit, QuickCheck, base, criterion, effect-handlers, free, hspec, hspec-discover, kan-extensions, mtl

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Andraz Bajt++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,49 @@+# effect-handlers++This is an extensible effects library for Haskell taking inspiration from the [Eff language](http://www.eff-lang.org/).++See these papers for the ideas and theory behind the library:++  - [O. Kammar et al: Handlers in Action!](http://homepages.inf.ed.ac.uk/slindley/papers/handlers.pdf)+  - [A. Bauer, M. Pretnar: Programming with Algebraic Effects and Handlers](http://arxiv.org/abs/1203.1539)+  - [O Kiselyov, A Sabry, C Swords: Extensible Effects](http://dl.acm.org/citation.cfm?id=2503791)++Implementation wise it's most close to [extensible-effects](http://hackage.haskell.org/package/extensible-effects) (also see the Extensible Effects paper) but it implements deep handlers instead of shallow.++## What does this library provide?+There is the `Eff` monad type and modules for pre-implemented effects.++  - `Exception`+  - `IO`+  - `Reader`+  - `Search`+  - `State`+  - `Writer`++It is easy to define your own effects and combine them.+++## Example+Most of the types are inferred, you only need to provide enough to tell the compiler how to specialize some effect handlers (e.g. readerHandler).++```haskell+import Control.Effects.Cont.Eff+import Control.Effects.Cont.Reader+import Control.Effects.Cont.Exception++program = do+  v <- ask+  if v < 15 +  then throw $ show v+  else return (v+1)++run n = runPure +                 . handle exceptionHandler +                 . handle (readerHandler n)++res :: Integer -> Either String Integer+res n = run n program+```++## Documentation+Haddock docs are [available online](http://edofic.github.io/effect-handlers)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/TestBench.hs view
@@ -0,0 +1,13 @@+module Main where++import Criterion.Main+import qualified Examples.Reader as Rd+import qualified Examples.Combined as Cmb+++main :: IO ()+main = do +  defaultMain [bgroup "test1" [ bench "reader" $ whnf Rd.prg1res 1],+               bgroup "test2" [ bench "combined" $ whnf Cmb.testPrgRes 100]+              ]+
+ effect-handlers.cabal view
@@ -0,0 +1,109 @@+name:                effect-handlers+version:             0.1.0.0+synopsis:            A library for writing extensible algebraic effects and handlers. Similar to extensible-effects but with deep handlers.+homepage:            https://github.com/edofic/effect-handlers+bug-reports:         https://github.com/edofic/effect-handlers/issues+license:             MIT+license-file:        LICENSE+author:              Andraz Bajt, Blaz Repas+maintainer:          Andraz Bajt <andraz@bajt.me>+category:            Control+build-type:          Simple+stability:           experimental+extra-source-files:  README.md+cabal-version:       >=1.10+description:+  This is an extensible effects library for Haskell taking inspiration from the Eff language <http://www.eff-lang.org/>.+  .+  See these papers for the ideas and theory behind the library:+  .+    - O. Kammar et al: Handlers in Action! <http://homepages.inf.ed.ac.uk/slindley/papers/handlers.pdf>+    - A. Bauer, M. Pretnar: Programming with Algebraic Effects and Handlers <http://arxiv.org/abs/1203.1539>+    - O Kiselyov, A Sabry, C Swords: Extensible Effects <http://dl.acm.org/citation.cfm?id=2503791>+  .+  Implementation wise it's most close to @extensible-effects@ <http://hackage.haskell.org/package/extensible-effects> (also see the Extensible Effects paper) but it implements deep handlers instead of shallow.+  .+  @+    import Control.Effects.Cont.Eff+    import Control.Effects.Cont.Reader+    import Control.Effects.Cont.Exception+    .+    program = do+      v <- ask+      if v < 15 +      then throw $ show v+      else return (v+1)+    .+    run n = runPure . handle exceptionHandler . handle (readerHandler n)+    .+    res :: Integer -> Either String Integer+    res n = run n program+  @+++library+  exposed-modules:     Data.Union+                     +                     , Control.Effects.Eff+                     , Control.Effects.Reader+                     , Control.Effects.Writer+                     , Control.Effects.State+                     , Control.Effects.Exception+                     , Control.Effects.IO+                     , Control.Effects.Search ++  -- other-extensions:    +  build-depends:       base           >=4.7 && <4.8+                     , free           >=4.9 && <4.10+                     , mtl            >=2.1 && <2.3+                     , kan-extensions >=4.1 && <4.2+  hs-source-dirs:      src+  default-language:    Haskell2010+  default-extensions:  RankNTypes+                     , KindSignatures+                     , DataKinds+                     , GADTs+                     , TypeOperators+                     , MultiParamTypeClasses+                     , FlexibleInstances+                     , FlexibleContexts+                     , NoMonomorphismRestriction+                     , DeriveFunctor+                     , DeriveDataTypeable+                     , GeneralizedNewtypeDeriving++test-suite spec+  type:                exitcode-stdio-1.0+  default-language:    Haskell2010+  hs-source-dirs:      test++  main-is:             Main.hs++  build-depends:       base >=4.7 && <4.8+                     , effect-handlers+                     , hspec >= 1.12 && <1.13+                     , QuickCheck+                     , HUnit+                     , hspec-discover++  other-modules:      Main+                    , Spec+                ++  default-extensions:  DataKinds+                     , FlexibleContexts+                     , NoMonomorphismRestriction+++benchmark benchm+  type:                exitcode-stdio-1.0+  default-language:    Haskell2010 +  hs-source-dirs:      bench, test+  main-is:             TestBench.hs+  build-depends:       base >=4.7 && <4.8, effect-handlers+  build-depends:       criterion+  ghc-options:         -O3+  default-extensions:  DataKinds+                     , FlexibleContexts+                     , NoMonomorphismRestriction+  
+ src/Control/Effects/Eff.hs view
@@ -0,0 +1,90 @@+-- |This implementation of the effect monad uses `Free` over a +-- `Union` of functors and then applies `Codensity` over it+-- for asymptotic improvements of ill-associated binds.+module Control.Effects.Eff+( Eff+, Handler+, Comp (Value, Comp)+, Res+, effect+, runPure+, runPureRes+, handle+, continue+, finish+, inj+, Member+, Typeable+) where++import Control.Applicative+import Control.Monad.Codensity+import Control.Monad.Free+import Data.Union+import Data.Typeable++-- |Result structure of the program is directly `Free` over `Union` +-- indexed by the list of effect functors.+type Res r = Free (Union r)++newtype Eff r a = Eff { runEff :: Codensity (Res r) a }+                  deriving (Functor, Applicative, Monad)++-- |Comp represents a computation. It is either a pure value or a computation +-- that needs further evaluation and effect handling. +data Comp e r a b = Value a | Comp (e (Res r b))++-- |Handler is a function that takes a result or an effect and a continuation+-- |and handles it. +-- +-- `e` is the effect functor you are handling+--+-- `r` represents the type of the type list of the remaining effects. +--  Usually you want to be polymorphic in this.+--+-- `a` is the result type of the program you will handle+--+-- `b` is the result of handled computation.+type Handler e r a b = Comp e r a b -> Res r b++-- | `effect` is meant to be used as a helper function for defining new effects.+-- See predefined effects for examples. Good way to use it is to pass in a lambda+-- expression with explicit `k` for continuation. You will need to manually `inj` +-- into the `Union` because of some GHC limitations.+effect :: (forall b . (a -> Res r b) -> Union r (Res r b)) -> Eff r a+effect e = Eff $ Codensity $ \k -> Free $ e k++-- |A program without effects is guaranteed to be pure so you +-- can safely convert it into a value.+runPure :: Eff '[] a -> a+runPure = runPureRes . finish++-- |Like `runPure` but for program results. You only need this for implementing+-- some handlers. +runPureRes :: Res '[] a -> a+runPureRes (Pure a) = a++-- |Finish a program and convert it into a result structure.+finish :: Eff r a -> Res r a+finish = lowerCodensity . runEff++-- |Convert a result back into a program in order to compose it.+-- This function might not be needed and might introduce some +-- performance issues (it is used in `handle`) but we didn't find +-- a way to drop it.+continue :: Res r a -> Eff r a+continue r = Eff $ Codensity (r >>=)++handleRes :: (Functor e, Typeable e) => Handler e r a b -> Res (e ': r) a -> Res r b+handleRes h (Pure a) = h $ Value a+handleRes h (Free u) = case decomp u of +  Left  u -> h $ Comp $ fmap (handleRes h) u+  Right u -> Free $ fmap (handleRes h) u++-- |Use a `Handler` on an `Eff` program to stripe away the first layer of effects.+-- There are some issues if you are using a handler that is somewhat polymorphic in `e`+-- As the compiler cannot figure out which effect are you handling. Currently the best +-- solution seems to be to manually specify type of the handler such that it is monomorphic+-- in `e`. Sorry.+handle :: (Functor e, Typeable e) => Handler e r a b -> Eff (e ': r) a -> Eff r b+handle h c = continue $ handleRes h $ finish c
+ src/Control/Effects/Exception.hs view
@@ -0,0 +1,26 @@+-- This module provides a simple exception throwind effect+-- with a single operation `throw` and two ready-made handlers+module Control.Effects.Exception where++import Control.Effects.Eff++-- |The functor representing the exception. You shouldn't need+-- to create this manually, just use `throw`.+newtype Exception m a = Exception m deriving (Functor, Typeable)++-- |Throw an exception. The only requirement is that exception+-- be typeable.+throw :: (Member (Exception a) r, Typeable a) => a -> Eff r b+throw m = effect $ \k -> inj $ Exception m++-- |This handler converts an program that might throw an exception+-- into a program that returns either its result or the exception.+exceptionHandler :: Handler (Exception m) r a (Either m a)+exceptionHandler (Value a) = return $ Right a+exceptionHandler (Comp (Exception m)) = return $ Left m++-- |This function generates a handler that upon a `throw` short-circuts+-- the computation and returns the default value instead.+defValueExceptionHandler :: a -> Handler (Exception m) r a a+defValueExceptionHandler _ (Value a) = return a+defValueExceptionHandler d (Comp (Exception m)) = return d
+ src/Control/Effects/IO.hs view
@@ -0,0 +1,27 @@+-- This module provides a mechanism to embed `IO` computations+-- into the effect monad. Note that the `IO` can only ever be at +-- the base of your stack (for the same reasons as with transformers).+module Control.Effects.IO where++import Control.Effects.Eff+++-- |The functor representing the effect. You shouldn't need+-- to create this manually, just use `liftIO`.+data LiftIO a = forall r . LiftIO (IO r) (r -> a) deriving (Typeable)++instance Functor LiftIO where+  fmap f (LiftIO c k) = LiftIO c (f . k)++-- |Lift an existing `IO` action into the effect monad.+liftIO :: Member LiftIO r => IO a -> Eff r a+liftIO c = effect $ \k -> inj $ LiftIO c k++-- |Handle by converting back to `IO`. Note that it is required that +-- the effect stack is otherwise empty - this handler would not+-- typecheck otherwise.+ioHandler :: Handler LiftIO '[] a (IO a)+ioHandler (Value a) = +  return $ return a+ioHandler (Comp (LiftIO c k)) = +  return $ c >>= (runPureRes . k)
+ src/Control/Effects/Reader.hs view
@@ -0,0 +1,21 @@+-- This module provides the familiar reader effect.+module Control.Effects.Reader where++import Control.Effects.Eff++-- |The functor representing the effect. You shouldn't need+-- to create this manually, just use `ask` or `reader`+newtype Reader w a = Reader (w -> a) deriving (Functor, Typeable)++-- |Get the value from the reader+ask :: (Member (Reader a) r, Typeable a) => Eff r a+ask = effect $ \k -> inj $ Reader k++-- |Lift a function into a reader.+reader :: (Member (Reader a) r, Typeable a) => (a -> b) -> Eff r b+reader f = effect $ \k -> inj $ Reader $ k . f++-- |The obvious handler that just embeds the value provided.+readerHandler :: w -> Handler (Reader w) r a a+readerHandler _ (Value a) = return a+readerHandler n (Comp (Reader k)) = k n
+ src/Control/Effects/Search.hs view
@@ -0,0 +1,44 @@+-- This module provides the familiar reader effect.+module Control.Effects.Search where++import Control.Effects.Eff+import Control.Monad+++-- |A proxy for passing type to functions. Example+-- > foo+data T a = T++asT :: a -> T a -> a+asT a T = a++-- |The functor representing the effect. You shouldn't need+-- to create this manually, just use `choose` or `searchFail`.+data Search w a = SChoose [w] (w -> a) deriving (Functor, Typeable)++-- |Nondeterministicaly choose an element from a list+choose :: (Member (Search w) r, Typeable w) => [w] -> Eff r w+choose ws = effect $ \k -> inj $ SChoose ws k++-- |Fail a search. Equal to choosing from an empty list.+searchFail :: (Member (Search w) r, Typeable w) => T w -> Eff r ()+searchFail t = do+  x <- choose []+  let _ = x `asT` t+  return ()++-- |Use a strict depth first search. Equal to using `ListT`+handleDFS :: Handler (Search w) r a [a] +handleDFS (Value a) = return [a]+handleDFS (Comp (SChoose ws k)) = f $ map k ws where+  f = foldr (liftM2 (++)) $ return []++-- |Lazy depth first search with backtracking.+handleBacktrackMaybe :: Handler (Search w) r a (Maybe a)+handleBacktrackMaybe (Value a) = return $ Just a+handleBacktrackMaybe (Comp (SChoose ws k)) = step ws where+  step [] = return Nothing+  step (w:ws') = +    k w >>= \r -> case r of +      m@(Just x) -> return m+      Nothing -> step ws'
+ src/Control/Effects/State.hs view
@@ -0,0 +1,35 @@+-- This module provides the familiar state effect.+module Control.Effects.State where++import Control.Effects.Eff+import Control.Monad+++-- |The functor representing the effect. You shouldn't need+-- to create this manually, just use `get`, `put` or `state`.+data State s a = SGet (s -> a) +               | SPut s a +               deriving (Functor, Typeable)++-- |Read from state+get :: (Member (State a) r, Typeable a) => Eff r a+get = effect $ \k -> inj $ SGet k++-- |Write to state+put :: (Member (State s) r, Typeable s) => s -> Eff r ()+put a = effect $ \k -> inj $ SPut a $ k ()++-- |Lift a function into state+state :: (Member (State s) r, Typeable s) => (s -> (a, s)) -> Eff r a+state f = do+  s <- get+  let (a, s') = f s+  put s'+  return a++-- |Handle state into a function. Note that applying the resulting +-- function you get out another program that you have to bind over.+stateHandler :: Handler (State s) r a (s -> Eff r a) +stateHandler (Value a) = return $ const $ return a+stateHandler (Comp (SGet k)) = return $ \s -> join $ ($s) `fmap` continue (k s)+stateHandler (Comp (SPut s k)) = return $ \_ -> join $ continue $ fmap ($s) k 
+ src/Control/Effects/Writer.hs view
@@ -0,0 +1,21 @@+-- This module provides the familiar writer effect.+module Control.Effects.Writer where++import Control.Effects.Eff+import Data.Monoid+++-- |The functor representing the effect. You shouldn't need+-- to create this manually, just use `tell`+data Writer m a = Writer m a deriving (Functor, Typeable)++-- |Send a value into the writer+tell :: (Member (Writer m) r, Typeable m) => m -> Eff r ()+tell m = effect $ \k -> inj $ Writer m $ k () ++-- |Handles writes by `mappend`ing them together.+writerHandler :: (Monoid m) => Handler (Writer m) r a (a, m)+writerHandler (Value a) = return (a, mempty)+writerHandler (Comp (Writer m k)) = do+  (a, m') <- k+  return (a, m <> m')
+ src/Data/Union.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverlappingInstances #-}++-- |This module provides an open union of functors.+module Data.Union +( Union+, Member+, inj+, prj+, decomp+, trivial+) where++import Data.Maybe+import Data.Typeable+import Unsafe.Coerce (unsafeCoerce)++-- |`Union` is an open sum of functors+-- A value of type `Union` r a is a value f a for some f that is a member of the r list+-- Since direct construction is not safe you have to use `inj` to create a value.+data Union (r :: [* -> *]) (a :: *) where+  Union :: (Functor f, Typeable f) => f a -> Union r a++instance Functor (Union r) where+  fmap f (Union fa) = Union (fmap f fa)++-- |The `Member` type clas denotes that f is a member of type list r+class Member (f :: * -> *) (r :: [* -> *]) where+instance Member h (h ': t)+instance (Member x t) => Member x (h ': t)++-- |Smart constructor for `Union`. Injects the functor into any union+-- of which the said functor is a member. Please note that only the +-- type constructor need be a `Typeable`.+inj :: (Typeable f, Functor f, Member f r) => f a -> Union r a+inj = Union ++-- |Project a `Union` into a specific functor.+prj :: (Typeable f, Member f r) => Union r a -> Maybe (f a)+prj (Union d) = res where+  availableType = typeOf1 d+  wantedType = typeOf1 $ fromJust res+  res = if availableType == wantedType+        then Just $ unsafeCoerce d+        else Nothing++-- |Decompose a `Union`. Similar to `prj` but gives you a+-- `Union` instance without the functor f in type if projection fails.+decomp :: (Typeable f) => Union (f ': r) a -> Either (f a) (Union r a)+decomp u@(Union d) = maybe (Right $ Union d) Left $ prj u++-- |A `Union` of one functor can only be that. Safe cast.+trivial :: (Typeable f) => Union '[f] a -> f a+trivial = fromJust . prj
+ test/Main.hs view
@@ -0,0 +1,9 @@+-- file test/Main.hs+module Main where++import Test.Hspec.Runner+import Test.Hspec.Formatters+import qualified Spec++main :: IO ()+main = hspecWith defaultConfig {configFormatter = Just progress} Spec.spec
+ test/Spec.hs view
@@ -0,0 +1,2 @@+-- file test/Spec.hs+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}