Skip to content

Instantly share code, notes, and snippets.

@agocorona
Created March 7, 2019 18:00
Show Gist options
  • Select an option

  • Save agocorona/cf14159916799ff24e48bdee09a8661a to your computer and use it in GitHub Desktop.

Select an option

Save agocorona/cf14159916799ff24e48bdee09a8661a to your computer and use it in GitHub Desktop.
Run pattern considered harmful

A lot of problems in haskell development is related with the problem of chaining effects. How do I insert an effect in a chain of computations?

Since the chain operation is bind, and has this signature:

  (>>=) :: m a -> (a -> m b) -> m b

then the two operands: the result of the chain and the rest of the chain should be in the same monad, so they should have the same effects.

To overcome this problem the monad transformer and the free monad use the "runXXX" and the lift pattern with XXX being the effect desired can run for some time:

runXXX :: t m a -> m a

lift :: m a -> t m a

so I can write: x >>= runXXX myCompXXX >>= y

for inserting XXX effect in a computation that do not use the XXX effect

or runXXXX $ myComp1XXX >>= lift noXXXeffect >>= myCom2XXX

to insert a computation that has not the XXX effect in a chain that uses it.

If I want a computation that uses the XXX effect and a new YYY effect,....

Monad transformers have an inherent limitation: they enforce the static ordering of effect layers and hence statically fixed effect interactions. There are practically significant computations that require interleaving of effects.

http://okmij.org/ftp/Haskell/extensible/index.html#MTL-drawbacks

Effect monads in the other side, allow interleaving of effects, in the sense that they can apply effects in an arbitrary order but since bind is defined as is, the effect signature to the runXXX primitive.

runXXX :: n a -> m a

a typical example using type level lists

runState :: m effect a -> stateValue -> m(state:effects) (a, stateValue')

set :: m effect a -> m(state:effect) a get :: m (Has state effects) -> m effects x

to use set and get I need runState as defined above so I can write:

    x >>=  runState (set "hello" >>= get) >>=  y

Essentially the problem is the same that in a transformer

However if bind is redefined as: >>=' :: a -> (a -> n b) -> m b

where the monad of the result may have a different type and thus, can express the new effect without using a runner, since I can use set and get without the runner:

x >>=' startEffect "hello >>=' put "world"  >>=' get >>=' y

so the signature of startEffect:

startEffect :: (NotMember state effects) -> m (state:effects) ()

and the whole operation once set is executed would have the state effect until the end of the computation.

There is no need for lifters and runners.

This in a conventional stack using the standard bind would be semantically equivalent to:

 x >>= runState "hello ( set "world" >>= get >>= lift y)

To be continued

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment