MonadPlus instance for Control.Eff when Exc is a member

In monad transformers ,

instance (Monad m, Monoid e) => MonadPlus (ExceptT e m)

      

In expandable effects , there is no such thing as

instance (Monoid e) => MonadPlus (Eff (Exc e :> r))

      

I tried to implement it, in vain. Here's what I have so far:

instance (Monoid e) => MonadPlus (Eff (Exc e :> r)) where
  mzero = throwExc mempty
  a `mplus` b = undefined $ do
                  resultA <- runExc a
                  case resultA of
                    Left l -> runExc b
                    Right r -> return $ Right r

      

There are 2 problems:

  • for mzero

    , GHC complains like this:

    Could not deduce (Monoid e0) arising from a use of β€˜mempty’
      from the context (Monad (Eff (Exc e :> r)), Monoid e)
    
          

    Why is GHC not compliant e0

    with e

    ?

    Answer (see comment): enableScopedTypeVariables

  • for mplus

    , undefined

    should be replaced with inverse function runExc

    , but I can't find it in the Extensible Effects API. Did I miss something?

Rationale : I want to be able to write a <|> b

inside Member (Exc e) r => Eff r a

, which means:

  • try it a

  • if a

    throws ea

    tryb

  • if b

    throws eb

    , then throwsmappend ea eb

This requires an instance Alternative

, so I'm trying to implement an instance MonadPlus

in the first place.

Note. I am using GHC 7.8.3.

Thank you in advance for your help.

+3


source to share


1 answer


I think you might be confused about the extensible effects and the desired instance MonadPlus (Eff (Exc e :> r))

shows confusion.

If you want to build a non-deterministic computation and also throw exceptions, you can do so without any new instances. I think I may be partially responsible for the confusion by defining separate mzero 'and mplus' which are completely equivalent to those in MonadPlus. Anyway, because of this equivalence, you can simply write

instance Member Choose r => MonadPlus (Eff r) where
    mzero = mzero'
    mplus = mplus'

      

The instance says: A computation that has a selection effect, among others, is an example of a MonadPlus computation. Let me emphasize the "among others" part. The calculation can have other effects, such as throwing exceptions. The MonadPlus instance above covers this case as well as all the others.

Thus, to use non-determinism and exceptions, you simply use mplus, mzero (or mplus ', mzero') along with throwExc. No need to define any new instances - in stark contrast to Monad Transformers.

Of course, you need to decide how you want your exceptions to interact with non-determinism: if the exception throws all options, or only the remaining selection? It depends on how you order your handlers, the effect is processed first. Alternatively, you can write a handler for both Select and Effect (to keep the choices already made to exclude and discard the rest - thus simulating the prologue). The library code (and the code accompanying the document) has examples of this.



Edit in response to modified question: If you need everything <|>

, it can be implemented simply, without MonadPlus, or cut. This statement is just a form of exception handling and is implemented as part of two catchError. Here is the complete code

alttry :: forall e r a. (Typeable e, Monoid e, MemberU2 Exc (Exc e) r) =>
          Eff r a -> Eff r a -> Eff r a
alttry ma mb =
  catchError ma $ \ea ->
  catchError mb $ \eb -> throwError (mappend (ea::e) eb)

      

If the computation of ma succeeds, its result is returned. Otherwise, mb is checked; it succeeds, its result is returned. If both fail, throw mappend-ed. The code is in line with the English specification.

We are using MemberU2 in the signature and not in the member make sure the computation only throws one type of exception. Otherwise, this construct is not very useful. I used the original Eff.hs implementation . This file also contains a test case.

By the way, with extensible effects, there is no need to define or use typeclasses like MonadPlus, MonadState, etc. These typeclasses were intended to hide the specific layout of the MonadTransformer stack. With stretchable effects, there's nothing to hide. Crutches are no longer needed.

+4


source







All Articles