Code Monkey home page Code Monkey logo

extensible-effects's Introduction

Extensible effects (Hackage, GHC)

Build Status Join the chat at https://gitter.im/suhailshergill/extensible-effects Stories in Ready Stories in progress

State and Error benchmarks Nondeterminism benchmarks

Implement effectful computations in a modular way!

The main monad of this package is 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.

Recommended Procedure:

  1. get extensible-effects by doing one of the following:
  • add extensible-effects as a dependency to a existing cabal or stack project
  • git clone https://github.com/suhailshergill/extensible-effects.git
  1. start stack ghci or cabal repl inside the project
  2. import Control.Eff and Control.Eff.QuickStart
  3. start with the examples provided in the documentation of the Control.Eff.QuickStart module

Tour through Extensible Effects

This section explains the basic concepts of this library.

The Effect List

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.

Writer, Reader and State all provide lazy and strict variants. Each has its own module that exposes a common interface. Importing one or the other controls whether the effect is strict or lazy in its inputs and outputs. It's recommended that you use the lazy variants by default unless you know you need strictness.

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

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.

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

import Control.Eff.State.{Lazy | Strict}

The state effect provides readable and writable state during a computation.

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 function fetches the current state and makes it available within subsequent computation. The put function sets the state to a 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

import Control.Eff.Reader.{Strict | Lazy}

The reader effect provides an environment that can be read. Sometimes it is considered as read-only state.

ask :: Member (Reader e) r => e -> Eff r e
runReader :: e -> Eff (Reader e ': r) a -> Eff r a

ask can be used to retrieve the environment provided to runReader from within a computation which has the Reader effect.

The Writer Effect

import Control.Eff.Writer.{Strict | Lazy}

The writer effect allows one to collect messages during a computation. It is sometimes referred to as write-only state, which gets retrieved at the end of the computation.

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 which folds over all written values. However, if you only want to collect 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.

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)
run2 = 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 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.

It might be convenient to include the necessary language extensions and disable class-constraint warnings in your project's .cabal file (or package.yaml if you're using stack).

Explanation is a work in progress.

Other Effects

Work in progress.

Integration with IO

IO or any other monad can be used as a base type for the Lift effect. There may be at most one instance of the Lift effect in the effects list, and it must be handled last. Control.Eff.Lift exports the runLift handler and lift function which provide the ability to run arbitrary monadic actions. Also, there are convenient type aliases that allow for shorter type constraints.

f :: IO ()
f = runLift $ do printHello
                 printWorld

-- These two functions' types are equivalent.

printHello :: SetMember Lift (Lift IO) r => Eff r ()
printHello = lift (putStr "Hello")

printWorld :: Lifted IO r => Eff r ()
printWorld = lift (putStrLn " world!")

Note that, since Lift is a terminal effect, you do not need to use run to extract pure values. Instead, runLift returns a value wrapped in whatever monad you chose to use.

Additionally, the Lift effect provides MonadBase, MonadBaseControl, and MonadIO instances that may be useful, especially with packages like lifted-base, lifted-async, and other code that uses those typeclasses.

Integration with Monad Transformers

Work in progress.

Writing your own Effects and Handlers

Work in progress.

Other packages

Some other packages may implement various effects. Here is a rather incomplete list:

Background

extensible-effects is based on the work of Extensible Effects: An Alternative to Monad Transformers. The paper and the followup freer paper contain details. Additional explanation behind the approach can be found on Oleg's website.

Limitations

Ambiguity-Flexibility tradeoff

The extensibility of Eff comes at the cost of some ambiguity. A useful pattern to mitigate this ambiguity is to specialize calls to effect handlers using type application or type annotation. Examples of this pattern can be seen in Example/Test.hs.

Note, however, that the extensibility can also be traded back, but that detracts from some of the advantages. For details see section 4.1 in the paper.

Some examples where the cost of extensibility is apparent:

  • Common functions can't be grouped using typeclasses, e.g. the ask and getState functions can't be grouped in the case of:

    class Get t a where
      ask :: Member (t a) r => Eff r a

    ask is inherently ambiguous, since the type signature only provides 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.

  • Code requires a greater number of type annotations. For details see #31.

extensible-effects's People

Contributors

aiya000 avatar bfops avatar gitter-badger avatar greydot avatar ibotty avatar mattaudesse avatar qnikst avatar samuelpilz avatar shineli1984 avatar spl avatar suhailshergill avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

extensible-effects's Issues

Add explicit implementation for `*>` to be compatible with new `forever`

The following code is a simple stacking of State Int and Lift IO. This is a simplification of very common pattern in applications with an event loop.

import Control.Eff.State.Lazy
import Control.Eff.Lift
import Control.Eff

compute :: (Member (State Int) r, SetMember Lift (Lift IO) r) => Eff r ()
compute = forever $ do
  i <- get
  lift $ putStrLn $ show (i :: Int)
  modify (succ :: Int -> Int)

main = runLift $ evalState compute (0 :: Int)

The problem with this code is that actually, if you run it, nothing happens.

The expected semantics would be that it starts printing out intermediate results endlessly, which it does not do. BTW the same behaviour is also observable when using Reader rather than State and now that I think of it, it probably also exhibits the same behaviour for just Lift IO as well.

The culprit in this case, is forever which is now implemented in base using Applicative.

forever     :: (Applicative f) => f a -> f b
forever a   = let a' = a *> a' in a'

Crucial here is that it uses *>, not >>. I have tested a custom forever using >> and that does exhibit the desired behaviour.

As far as I could discern it the issue lies in the default implementation of *>.

(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2

As you can see it uses <*> internally which means it needs the result of a2 before it can actually return a result. This, I think, means it has to evaluate the free monad to the right completely, which is impossible.

I think it would be a good idea to add a custom implementation for *> (and <* while we're at it) that is lazier and does not evaluate the entire monad.

GHC 7.10.1 errors in version on hackage

I'm actually a bit confused that #34 implies that the package does build on ghc 7.10.1 (albeit with warnings). I see the following error instead:

[10 of 20] Compiling Control.Eff.Cut  ( src/Control/Eff/Cut.hs, dist/dist-sandbox-6b04e7fe/build/Control/Eff/Cut.o )

src/Control/Eff/Cut.hs:70:2:
    Illegal equational constraint Data.OpenUnion.Internal.OpenUnion2.Member'
                                    Choose r
                                  ~ 'True
    (Use GADTs or TypeFamilies to permit this)
    When checking that loop has the inferred type
      loop :: forall r a.
              (Data.OpenUnion.Internal.OpenUnion2.Member' Choose r ~ 'True) =>
              [Eff (Exc CutFalse :> r) a]
              -> Free (Union (Exc CutFalse :> r)) a -> Eff r a
    In an equation for call’:
        call
          = loop []
          where
              loop jq
                = freeMap
                    (\ x -> return x `mplus'` next jq)
                    (\ u
                       -> case decomp u of {
                            Right (Exc CutFalse) -> ...
                            Left u' -> ... })
              check ::
                Member Choose r =>
                [Eff (Exc CutFalse :> r) a]
                -> Union r (Eff (Exc CutFalse :> r) a) -> Eff r a
              check jq u | Just (Choose [] _) <- prj u = next jq
              check jq u | Just (Choose [x] k) <- prj u = loop jq (k x)
              check jq u | Just (Choose lst k) <- prj u = next $ map k lst ++ jq
              check jq u = send u >>= loop jq
              next :: Member Choose r => [Eff (Exc CutFalse :> r) a] -> Eff r a
              ....
Failed to install extensible-effects-1.9.1.1

And then adding LANGUAGE GADTs to that module then leaves a whole bunch of other "Could not deduce" errors.

RFC: Handle class

It would be nice to read on the motivation behind this class, as well as have a manual on how to implement effects with it. Right now the latter part is not entirely obvious.

`SetMember Lift (Lift IO) r` doesn't seem to be sufficient to line up the function context for lift.

I'm currently isolating IO parts of my software into Eff. In the process I've encountered to seemingly (to me) identical usages and only in one can the function's context be aligned. I've attempted many variants, including a "Getter" effect that just reads lines off the console which also fails. I've tried the Logger effect as a data Logger n = Logger String n deriving (Tapeable, Functor) I've tried with concrete types and with quasi generic types, even supplying the putStrLn call in the send . inj statement. My hope is that I've just done something silly, but I don't see how these two examples differ in a way that is substantive enough to cause the compile to break.

Example One (this one works fine):

main :: IO ()
main = do
   pool <- HP.acquire (4, 60.0, hset)
   r <- runLift $ runHasql pool $
      {-db $ appendMessage "Hello World!"-}
      db $ getMessage 3
   print r
   where
      hset = H.settings "localhost" 5432 "andrew" "" "test_eff"

data Hasql n
   = forall a. Hasql (H.Session a) (a -> n)
   deriving (Typeable)

deriving instance Functor Hasql

db :: (Member Hasql e) => H.Session a -> Eff e a
db act = send . inj $ Hasql act id

runHasql
   :: SetMember Lift (Lift IO) r
   => HP.Pool -> Eff (Hasql :> r) a -> Eff r (Either HP.UsageError a)
runHasql pool = loop
   where
      loop = freeMap
               (return . Right)
               (\u -> handleRelay u loop act)
      act (Hasql s k) = (lift $ HP.use pool s) >>= \case
         Left  e -> return $ Left e
         Right v -> loop (k v)

Example Two (this one doesn't work):

main :: IO ()
main = do
   r <- runLift $ runConsoleLogging $ do
      logM ("Hello"::String)
      logM ("World"::String)
      return (1 + 2)
   print (r::Int)

data Logger n
   = Logger String (() -> n)
   deriving (Typeable)

deriving instance Functor Logger

logM :: (Member Logger r) => String -> Eff r ()
logM msg = send . inj $ Logger msg id

runConsoleLogging
   :: SetMember Lift (Lift IO) r
   => Eff (Logger :> r) a -> Eff r a
runConsoleLogging = loop
   where
      loop = freeMap
               return
               (\u -> handleRelay u loop act)
      act (Logger msg k) = do
         lift $ putStrLn msg       -- compile error is here
         loop (k ())

The error:

/Users/andrew/src/test-eff/src/ConsoleExample.hs:43:10:
    Could not deduce (extensible-effects-1.11.0.3:Data.OpenUnion.Internal.Base.MemberUImpl
                        extensible-effects-1.11.0.3:Data.OpenUnion.Internal.OpenUnion2.OU2
                        Lift
                        (Lift IO)
                        r)
      arising from a use of ‘lift’
    from the context (SetMember Lift (Lift IO) r)
      bound by the type signature for
                 runConsoleLogging :: SetMember Lift (Lift IO) r =>
                                      Eff (Logger :> r) a -> Eff r a
      at /Users/andrew/src/test-eff/src/ConsoleExample.hs:(35,7)-(36,36)
    In the expression: lift
    In a stmt of a 'do' block: lift $ putStrLn msg
    In the expression:
      do { lift $ putStrLn msg;
           loop (k ()) }

allow (de)serializing internal Eff r state

i want to interface with wai and generate a Middleware :: Request -> IO Response from a SetMember Lift (Lift IO) r => Request -> Eff r Response action.

i looked into what yesod is doing (which for that discussion reduces to having a an action Request -> ResourceT IO Response). yesod gets ResourceT's internal state (aptly named InternalState and restores it afterwards using functions (specialized to IO):

withInternalState :: (InternalState -> IO a) -> ResourceT IO a
createInternalState :: IO InternalState
closeInternalState :: InternalState -> IO ()

see the hackage documentation.

can something similar be done with Eff r? can one somehow expose the underlying VE w r and will that suffice to reconstruct Eff r's state?

that might also lead to insights regarding the catch and mask semantics i want in my other bug. given that functionality, one could use runLift to implement catch with the existing catch from Control.Exception. that way one could give an instance for MonadCatch and MonadMask from exceptions edit: and more generally for MonadBaseControl from monad-control.

-Werror

Numerous modules in extensible-effects have -Werror enabled unconditionally. Given that there is a cabal flag for that now, maybe the pragmas in those modules should be removed?

This change will make life a bit easier when using development tools like Intero.

Undeclared `TypeApplications` extension

This caused failures for GHCs lacking this extension:

Configuring library for extensible-effects-5.0.0.1..
Preprocessing library for extensible-effects-5.0.0.1..
Building library for extensible-effects-5.0.0.1..

src/Control/Eff/Logic/NDet.hs:12:14:
    Unsupported extension: TypeApplications
<<ghc: 120132488 bytes, 93 GCs, 2975444/7807312 avg/max bytes residency (5 samples), 16M in use, 0.001 INIT (0.001 elapsed), 0.041 MUT (0.041 elapsed), 0.063 GC (0.063 elapsed) :ghc>>

I've already fixed up the metadata for the affected releases (i.e. there's no need to upload a costly release to Hackage -- it's already fixed on Hackage):

You can inspect the current Hackage build-plan health at Hackage Matrix CI

Let me know if you have any questions.

Please expose Union's constructor

Please expose Union's constructor (possibly through an .Internal module).

I'm writing a handler that can handle many effects, and looking them up in a table is more efficient (and simple) than writing multiple prjs.

Build failure with GHC 8

> /tmp/stackage-build8$ stack unpack extensible-effects-1.11.0.3
Unpacked extensible-effects-1.11.0.3 to /tmp/stackage-build8/extensible-effects-1.11.0.3/
> /tmp/stackage-build8/extensible-effects-1.11.0.3$ runghc -clear-package-db -global-package-db -package-db=/home/stackage/work/builds/nightly/pkgdb Setup configure --package-db=clear --package-db=global --package-db=/home/stackage/work/builds/nightly/pkgdb --libdir=/home/stackage/work/builds/nightly/lib --bindir=/home/stackage/work/builds/nightly/bin --datadir=/home/stackage/work/builds/nightly/share --libexecdir=/home/stackage/work/builds/nightly/libexec --sysconfdir=/home/stackage/work/builds/nightly/etc --docdir=/home/stackage/work/builds/nightly/doc/extensible-effects-1.11.0.3 --htmldir=/home/stackage/work/builds/nightly/doc/extensible-effects-1.11.0.3 --haddockdir=/home/stackage/work/builds/nightly/doc/extensible-effects-1.11.0.3 --flags=
Configuring extensible-effects-1.11.0.3...
> /tmp/stackage-build8/extensible-effects-1.11.0.3$ runghc -clear-package-db -global-package-db -package-db=/home/stackage/work/builds/nightly/pkgdb Setup build
Building extensible-effects-1.11.0.3...
Preprocessing library extensible-effects-1.11.0.3...
[ 1 of 22] Compiling Data.OpenUnion.Internal.Base ( src/Data/OpenUnion/Internal/Base.hs, dist/build/Data/OpenUnion/Internal/Base.o )

src/Data/OpenUnion/Internal/Base.hs:61:1: error:
    • Potential superclass cycle for ‘MemberImpl’
        one of whose superclass constraints is headed by a type family:
          ‘MemberConstraint impl t r’
      Use UndecidableSuperClasses to accept this
    • In the class declaration for ‘MemberImpl’

src/Data/OpenUnion/Internal/Base.hs:65:1: error:
    • Potential superclass cycle for ‘MemberUImpl’
        one of whose superclass constraints is headed by a type family:
          ‘MemberUConstraint impl t r’
      Use UndecidableSuperClasses to accept this
    • In the class declaration for ‘MemberUImpl’

Chain multiple 'runError' calls into a single one.

Just describing an idea I want to work on.

It would be call if instead of multiple chains of 'runError' handlers we also provide a handler that handles all exception in the list and then returns something like Result errs a where errs is a type-level list, and the whole Result is similar to Either except it has a polymorphic variant on the left.

RFC: Version semantics

How are version numbers assigned in this library? Is this a kind of semantic versioning? If so, then why four numbers and not three?

MonadError, MonadThrow, MonadCatch, MonadMask instances for module Exception

I suggest adding implementation of the MonadError, MonadThrow, MonadCatch, MonadMask instances in the module Control.Eff.Exception. This will allow you to use the functions of the such modules as Control.Monad.Catch, for exampe bracket.

type SomeExc = Exc SomeException

runSomeExc :: Eff (SomeExc:> r) a -> Eff r (Either SomeException a)
runSomeExc = runExc

instance (Member SomeExc r) => MonadThrow (Eff r) where
    throwM = throwExc . toException

instance (Member SomeExc r) => MonadCatch (Eff r) where
    catch eff f = catchExc eff $ \e -> maybe (throwExc e) f $ fromException e

instance (Exception e,Member (Exc e) r,SetMember Exc (Exc e) r) => MonadError e (Eff r) where -- SetMember due to
    throwError = throwExc                                                                     -- functional dependency 
    catchError = catchExc                                                                     -- in MonadError

instance (Member SomeExc r) => MonadMask (Eff r) where
  mask a = a id
  uninterruptibleMask a = a id

It can also be useful

liftWithExc :: forall r m a. (Typeable m, Member SomeExc r,SetMember Lift (Lift m) r,MonadCatch m) =>
                m a -> Eff r a
liftWithExc = liftEitherM . (Control.Monad.Catch.try  :: m a -> m (Either SomeException a))

for making single exit point for exceptions.

No type inference in some cases

When using ext-eff, I find I have to add type annotations more than I'd like
For example (with GHC 7.8.4, ext-eff 1.9.0.1):

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Eff.Reader.Lazy
import Data.Functor

-- f can be fed with anything showable, notably Int
f :: (Show a) => a -> ()
f _ = ()

-- g1 fails to compile
g1 :: (Member (Reader Int) r) => Eff r Bool
g1 = do
  f <$> ask
  return True

-- g2 fails to compile
g2 :: (Member (Reader Int) r) => Eff r Bool
g2 = do
  f <$> (ask :: Eff r Int)
  return True

-- g3 compiles
g3 :: (Member (Reader Int) r) => Eff r Bool
g3 = do
  (f :: Int -> ()) <$> ask
  return True

It looks to me that the g1 implementation has all the necessary information for GHC to infer that I'm asking for an Int. Strangely enough, type annotating ask doesn't work either.
I'm not sure whether this is an issue in extensible-effects or if it is inherent to GHC's type inference. Can you assist ?

For information, the build errors are the following:

  • for g1:
Could not deduce (Show a0) arising from a use of ‘f’
    from the context (Member (Reader Int) r)
      bound by the type signature for
                 g1 :: Member (Reader Int) r => Eff r Bool
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Show Control.Exception.Base.NestedAtomically
        -- Defined in ‘Control.Exception.Base’
      instance Show Control.Exception.Base.NoMethodError
        -- Defined in ‘Control.Exception.Base’
      instance Show Control.Exception.Base.NonTermination
        -- Defined in ‘Control.Exception.Base’
      ...plus 241 others
    In the first argument of ‘(<$>)’, namely ‘f’

Overlapping instances for Member (Reader a0) r
      arising from a use of ‘ask’
    Matching givens (or their superclasses):
      (Member (Reader Int) r)
        bound by the type signature for
                   g1 :: Member (Reader Int) r => Eff r Bool
    Matching instances:
      instance extensible-effects-1.9.0.1:Data.OpenUnion.Internal.OpenUnion2.Member'
                 t r
               ~ 'True =>
               Member t r
        -- Defined in ‘extensible-effects-1.9.0.1:Data.OpenUnion.Internal.OpenUnion2’
    (The choice depends on the instantiation of ‘r, a0’)
    In the second argument of ‘(<$>)’, namely ‘ask’
  • for g2:
Could not deduce (extensible-effects-1.9.0.1:Data.OpenUnion.Internal.OpenUnion2.Member'
                        (Reader Int) r1
                      ~ 'True)
    from the context (Member (Reader Int) r)
      bound by the type signature for
                 g2 :: Member (Reader Int) r => Eff r Bool
    In the second argument of ‘(<$>)’, namely ‘(ask :: Eff r Int)’

strange parameter order for VE

VE is a free monad, but with type parameters swapped around. Is there a good reason for the swap? It's quite confusing.

Remove usage of deprecated OverlappingInstances

Using OpenUnion52.hs results in the following build error with make test

[ 2 of 20] Compiling Control.Eff.NdetEff.Test ( test/Control/Eff/NdetEff/Test.hs, dist/build/extensible-effects-tests/extensible-effects-tests-tmp/Control/Eff/NdetEff/Test.o )

test/Control/Eff/NdetEff/Test.hs:54:53: error:
    • Couldn't match type ‘Writer [Char]’ with ‘NdetEff’
        arising from a use of ‘tsplit’
    • In the first argument of ‘runListWriter’, namely ‘tsplit’
      In the second argument of ‘($)’, namely ‘runListWriter tsplit’
      In the second argument of ‘($)’, namely
        ‘makeChoiceA $ runListWriter tsplit’
   |
54 |       tsplitr20 = run $ makeChoiceA $ runListWriter tsplit
   |                                                     ^^^^^^

test/Control/Eff/NdetEff/Test.hs:55:61: error:
    • Couldn't match type ‘Writer [Char]’ with ‘NdetEff’
        arising from a use of ‘tsplit’
    • In the first argument of ‘msplit’, namely ‘tsplit’
      In the first argument of ‘(>>=)’, namely ‘msplit tsplit’
      In the first argument of ‘runListWriter’, namely
        ‘(msplit tsplit >>= unmsplit)’
   |
55 |       tsplitr21 = run $ makeChoiceA $ runListWriter (msplit tsplit >>= unmsplit)

ignoring the above and continuing with the make recipe results in another compilation error further along

[ 3 of 20] Compiling Control.Eff.Operational.Test ( test/Control/Eff/Operational/Test.hs, dist/build/extensible-effects-tests/extensible-effects-tests-tmp/Control/Eff/Operational/Test.o )

test/Control/Eff/Operational/Test.hs:25:39: error:
    • Could not deduce (Data.OpenUnion.FindElem (Program Jail) r)
        arising from a use of ‘prog’
      from the context: (Member (State [String]) r,
                         Member (Writer String) r)
        bound by the type signature for:
                   comp :: forall (r :: [* -> *]).
                           (Member (State [String]) r, Member (Writer String) r) =>
                           Eff r ()
        at test/Control/Eff/Operational/Test.hs:(22,7)-(24,25)
    • In the second argument of ‘runProgram’, namely ‘prog’
      In the expression: runProgram adventPure prog
      In an equation for ‘comp’: comp = runProgram adventPure prog
   |
25 |       comp = runProgram Eg.adventPure Eg.prog

ignoring above we get errors in Test/Lift. there clearly are some deficiencies in the current implementation of OpenUnion52.

lift and bracket pattern

hi,

i hope it's ok to use the issue tracker for support.

maybe i am missing things, but how can i use bracket-like functions within a (lifted-IO) effect?

i'd like a function bracket :: SetMember Lift (Lift IO) r => Eff r a -> (a -> Eff r b) -> (a -> Eff r c) -> Eff r c, or more specific SetMember Lift (Lift IO) r => IO a -> (a -> IO b) -> (a -> IO c) -> Eff r c, but i could not construct it easily from Control.Exception.bracket. when trying to implement this bracket myself, i ran into problems with mask.

Or is the idiomatic way to use a specific effect? so you might have e.g. (not thought through) runBracket which will only open/close the resources and convert all the exceptions to Control.Eff.Exception.Exc using throwExc and try. that's nice and explicit of course, but you risk missing some exceptions and not closing the resource.

how to port `SetMember` effects from 1.2 to 1.5

the following works in 1.2

test :: (Monad m, Typeable1 m, SetMember Lift (Lift m) r) => Eff r Int
test = lift $ return 42

but the following mechanical translation to 1.5 is ambiguous

test :: (Monad m, Typeable1 m, Member (Lift m) r) => Eff r Int
test = lift $ return 42

not giving a type signature does not fix it (even without monomorphism restriction).

MonadBase and MonadBaseControl instances

These instances would be useful but they are missing. I checked the log and found that MonadBase instance was removed not so long ago (a6d3dc7) without any explanation.

Would a PR reintroducing MonadBase and adding MonadBaseControl instance be welcome?

Trustworthy related warning on ghc-7.10

If compat/ghc-7.10 branch is build on ghc 7.10 there are a bunch of warnings:

on the commandline: Warning:
    `Control.Eff.Writer.Strict' is marked as Trustworthy but has been inferred as safe!

This happens because starting from 7.10 ghc checks if module is more safe than it's marked.
The problem here is that there is no obvious simple solution for the case when Trustworthy is set in default-extenstion, because adding {-# LANGUAGE Safe #-} will conflict with Trustworthy and there is no NoTrustworthy pragma to disable Trustworthy.

As a solution I'd suggest removing Trustworthy from default-extensions and set it only in relevant modules.

Documentation: Detailed Introduction to all Effects

As I can see, there is no documentation that presents the effects implemented in this package in Detail and presents usages. Sure, there is an Example module but I did not learn anything from that.

Currently, learning extensible-effects is only possible if one already understands MTL. The goal is to make this package more approachable.

I would like to add:

  • more comprehensive package description
    • currently this is a single bullet point of advantages and some limitations)
    • The package description should contain at least some
  • A tutorial- or quickstart- module similar to tutorials of other packages.
  • A conclusive List of the effects implemented and their implementations, However, I have not decided where to put that (this is completely missing).
  • A guide on how to implement a custom effect (this is extensible-effects after all 😄), but this should not be too visible for newcommers (Writing your own Effects and Handlers in README.md)

However, this will take several weeks to do on my part. Are regular status-updates desired? Feedback and reviews are welcome. (my wording is not always the best)

I have good understanding of the basic effects State, Writer, Reader. But have problems for others.

I get the intent of Trace and Fresh, but do not get why it is implemented in the way it is. I have ideas about Choose and NdetEff but what is the difference? It says "Another implementation of nondeterministic choice effect" in the documentation, but an explanation, is missing what is different.

I absolutely do not get the Cut, Coroutine and Operational effects, but will have a deeper look into them.

@suhailshergill are there any other resources for the effects that you could link to? Or could you explain some design decisions made for the API?

call for maintainer

Hello all, over the next few months I might not be in a position to be an active maintainer (due to personal reasons). I would like to ensure that in the meanwhile there is minimal effect on the library. Specifically, bug-fixes and updates get pushed in a timely manner.

Could someone volunteer to be an additional maintainer? Shout out to @greydot @mattaudesse @sheyll, @power-fungus

Thanks

Argument ordering of run* functions

I know MTL and others have it similar, but I do not understand the argument orderung of the run* functions.

Assume I have an effectful computation I want to run. computation :: (Member (State a) e, Member (Reader b) e) => Eff e ()), similarly construcs are possible.

This is done using run $ execState (runReader computation context) initialState, however I perfer to write it as run . flip execState initialState . flip runReader context $ computation

Is it a possibility to add functions that have the arguments flipped so that the flips can be removed? Or can someone explain to me why the functions have the arguments in that order

Incorporate updates from recent paper (i.e. use alternate definition of Free w/o Functor constraint)

the above would address the point regd. Lazy writer and Lazy state effects made by @ekmett in https://www.reddit.com/r/haskell/comments/387ex0/are_extensible_effects_a_complete_replacement_for/crt1pzm as can be seen in code example provided by oleg below:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

-- On-demand state computation:
-- example taken from edward kmett's comment here:
-- <http://www.reddit.com/r/haskell/comments/387ex0/are_extensible_effects_a_complete_replacement_for/crt1pzm>

-- The state here is lazy and not extensible: neither it was in ekmett's
-- comments

-- Extensible effects make it clear that where the computation is delayed
-- (which I take as an advantage) and they do maintain the degree of
-- extensibility (the delayed computation must be effect-closed, but the
-- whole computation does not have to be).

module LazyState where

import Eff1
import OpenUnion41


-- define a new effect for state on-demand (in ExtEff, the state is
-- by default strict -- as it should be if we want the predictable performance
-- and effect sequencing)

data LazyState s v where
  Get :: LazyState s s
  Put :: s -> LazyState s ()
  Delay :: Eff '[LazyState s] a  -> LazyState s a --  Eff as a transformer


runStateLazy :: s -> Eff '[LazyState s] a -> Eff '[] (a,s)
runStateLazy s = handle_relay_s s (\s x -> return (x,s))
                   (\s req k -> case req of
                       Get   -> unArr (k s) s
                       Put s -> unArr (k s) ()
                       Delay m -> let ~(x,s1) = run $ runStateLazy s m
                                  in unArr (k s1) x)

lget = send Get
lput = send . Put

onDemand :: Eff '[LazyState s] v -> Eff '[LazyState s] v
onDemand = send . Delay


lex1 = do
  onDemand lex1
  lput (1::Int)

ex1Run :: ((),Int)
ex1Run = run $ runStateLazy 0 lex1
-- ((),1)

-- modify as Delay

lmodify :: (s->s) -> Eff '[LazyState s] ()
lmodify f = onDemand $ do
  s <- lget
  lput (f s)


lex2 = do
  lmodify ((1::Int):)
  lmodify ((2::Int):)
  lget

ex2Run :: ([Int],[Int])
ex2Run = run $ runStateLazy [] lex2
-- ([2,1],[2,1])

-- Edward's example

lex3 = do
  onDemand lex3
  lmodify ((1::Int):)

ex3Run :: ((),[Int])
ex3Run = let (x,s) = run $ runStateLazy [] lex3
         in (x,take 5 s)

-- ((),[1,1,1,1,1])

-- a bit more interesting
lex4 :: Eff '[LazyState [Int]] [Int]
lex4 = do
  onDemand lex4
  lmodify ((1::Int):)
  lmodify ((2::Int):)
  lget

ex4Run :: ([Int],[Int])
ex4Run = let (x,s) = run $ runStateLazy [] lex4
         in (take 7 $ x,take 5 $ s)

-- ([2,1,2,1,2,1,2],[2,1,2,1,2])

at least Exc does not seem to work with ghc 7.4

hi,

while exploring extensible effects i could not get it to work with ghc 7.4 (the same code works with ghc 7.6). i did not look into other effects so far.

example:

testExc :: Member (Exc String) r => Int -> Eff r a
testExc 0 = throwExc "Zero given"
testExc n = return n

yields the following error message.

Expecting one more argument to `r'
    In the type signature for `testExc':
      testExc :: Member (Exc String) r => Int -> Eff r a

without the type signature the ambiguity check fails

    Could not deduce (Member (* -> *) * (Exc e0) r)
      arising from the ambiguity check for `testExc'
    from the context (Typeable e,
                      Data.String.IsString e,
                      Member (* -> *) * (Exc e) r)
      bound by the inferred type for `val1':
                 (Typeable e, Data.String.IsString e,
                  Member (* -> *) * (Exc e) r) =>
                 Int -> Eff r Int
      at Exception.hs:(33,1)-(34,14)
    Possible fix:
      add an instance declaration for (Member (* -> *) * (Exc e0) r)
    When checking that `testExc'
      has the inferred type `forall e r.
                             (Typeable e, Data.String.IsString e,
                              Member (* -> *) * (Exc e) r) =>
                             Int -> Eff r Int'
    Probable cause: the inferred type is ambiguous

convenience functions (à la errors package)

i am exploring using extensible effects for errors and became fond of using e.g. fmapLT :: Monad m => (a -> b) -> EitherT a m r -> EitherT b m r from errors to integrate different exceptions types in application code.

so i was missing a few functions like the following.

coerceExc :: (Typeable e, Typeable e', Member (Exc e') r) =>
  (e -> e') -> Eff (Exc e :> r) a -> Eff r a
coerceExc t eff = runExc eff >>= either (throwExc . t) return

liftEither :: (Typeable e, Member (Exc e) r) => Either e a -> Eff r a
liftEither (Left e) = throwExc e
liftEither (Right a) = return a

liftEitherM :: m (Either e a) -> Eff r a
liftEitherM action = undefined -- to be done :D

there might also be some function to convert between between Exc and Fail.

what do you think?

rfc: add log effects

hi,

i will send a pull request, if you deem this approach worthy, so don't feel pushed to implement it yourself as last time :D.

the following is a request for comment on adding a log effect. it is meant to be as extensible as needed to support many different scenarios:

  • (pure) logging into [String]
  • to file/stderr/network
  • using existing haskell libraries
  • logging different types, e.g. json messages
  • different log levels or even facilities

the api could be nicer, but i couldn't figure out how to use type classes for the formatting function. ghc 7.6.3 always lost the typeclass information, except when using explicit type annotations for runLog, i.e. runLog' :: Eff (SimpleLog :> r) -> Eff r (a, [String]). i don't know whether that is a drawback of using Typeable.

the essence of the implementation is the following.

data Log a b v = Log a b v
  deriving (Typeable, Functor)

-- | not exported, just for here for clarity
logNext :: Log a b v -> v
logNext (Log _ _ v) = v

logTo  :: (Typeable f, Typeable c, Member (Log f c) r) => f -> c -> Eff r ()
logTo facility line = send $ \next -> inj (Log facility line (next ()))

runLog :: (Typeable l, Typeable t) =>
    (forall v. Log l t v -> String) -> Eff (Log l t :> r) a -> Eff r (a, [String])
runLog formatter = go . admin
  where go (Val v) = return (v, [])
        go (E req) = handleRelay req go performLog
        performLog l = fmap (prefixLogWith (formatter l)) (go (logNext l))
        prefixLogWith txt (v, l) = (v, txt : l)

runLogStdErr ::
  (Typeable l, Typeable t,
   SetMember Lift (Lift IO) r) =>
  (forall v. Log l t v -> String) -> Eff (Log l t :> r) a -> Eff r a
runLogStdErr formatter = runLogM (putStrLn . formatter)

runLogM ::
  (Typeable l, Typeable t, Typeable1 m,
   SetMember Lift (Lift m) r) =>
  (forall v. Log l t v -> m ()) -> Eff (Log l t :> r) a -> Eff r a
runLogM logger = go . admin
  where go (Val v) = return v
        go (E req) = handleRelay req go performLog
        performLog l = lift (logger l) >> go (logNext l)

with (in maybe Control.Effect.Log.Simple):

data Severity = Panic | Alert | Critcal | Err | Warning | Notice | Info | Debug
  deriving (Show, Typeable)

type SimpleLog = Log Severity String

runSimpleLog :: Member SimpleLog r => Eff (SimpleLog :> r) a -> Eff r (a, [String])
runSimpleLog = runLog showLog'

runSimpleLogStdErr :: (SetMember Lift (Lift IO) r, Member SimpleLog r) =>
    Eff (SimpleLog :> r) a -> Eff r a
runSimpleLogStdErr = runLogStdErr showLog'

showLog' :: SimpleLog v -> String
showLog' (Log sev line _) = "[" ++ show sev ++ "] " ++ line

what do you think?

Missing license

@bfops the extensible-effects is missing a license file. If you could specify a license type/content, i can take care of the rest

Provide OpenUnion2

Since closed type families are available in latest GHC, could you please provide the OpenUnion2 module in extensible-effects ?
Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.