0

Trying to define bind for this monad in haskell. If the calculation succeeds it calls the SCont continuation, if it fails it calls the FCont continuation.

type M a = SCont a -> FCont -> Answer
type SCont a = a -> FCont -> Answer
type FCont = () -> Answer

result :: a -> M a
result x ks kf = ks x kf

(>>=) :: M a -> (a -> M b) -> M b
David Furlong
  • 1,343
  • 1
  • 12
  • 15
  • Have you tried using typed holes? Where did you run into trouble? – dfeuer Mar 30 '16 at 14:17
  • 2
    You can't define "bind" for a bare type synonym. You need to make it a newtype, and then declare it an Instance of Monad. – Paul Johnson Mar 30 '16 at 14:17
  • 1
    Also, why would you want `() -> Answer` instead of just `Answer`, and what is `Answer` anyway? – dfeuer Mar 30 '16 at 14:18
  • Yeah I know I need to make it an instance of Monad, but putting Haskell formalities aside, how would I define it. Answer can be anything. – David Furlong Mar 30 '16 at 14:21
  • I'm trying to learn to define bind given a Monad. This is from an old problem sheet. Why I would want to do this in practice is fairly irrelevant to me – David Furlong Mar 30 '16 at 14:22
  • Start by ditching `FCont`. It's silly noise. Use `Answer` instead. Have you tried typed holes yet? Where did you get stuck? – dfeuer Mar 30 '16 at 14:25
  • Trying typed holes right now, thanks for the suggestion – David Furlong Mar 30 '16 at 14:27
  • Using typed holes didn't really do much except tell me that haskell bind expected a M b type on the RHS of the def. (which is obvious given the type signature). Ditching FCont just gives me the monad of continuations, in which (x >>= f) k = xm (\x -> f x k). However I can't see how this helps me – David Furlong Mar 30 '16 at 14:57
  • Perhaps I'm using typed holes wrong – David Furlong Mar 30 '16 at 14:57
  • The reason I suggested ditching `FCont` is that `() -> a` is "morally equivalent" to `a`, for any type `a`. Because Haskell is lazy, the near-isomorphism almost preserves performance characteristics. So the only differences are: 1. `() -> a` has an extra bottom element observable only by `seq` and 2. The result of `() -> a` may not be memoized (but that's implementation-dependent). In a strict language like ML, the distinction matters much more. – dfeuer Mar 30 '16 at 15:58

2 Answers2

2

Figured it out, thanks for trying to help

(xm >>= f) ks kf = xm (\a f' -> f a ks f') kf

David Furlong
  • 1,343
  • 1
  • 12
  • 15
1

The definition you give of SCont is weird to me: how comes the successful continuation takes the failure one as an argument? Plus, given you return and bind, it looks like the failure one is threaded all the way down the call tree and... applied to the success one in case you hit a return.

I would expect the 'double barrelled continuation Monad' to be defined like so:

module SFCont where

import Control.Monad

newtype SFCont r a = SFCont { sfCont :: SCont r a -> FCont r a -> r }
type SCont r a = a -> r
type FCont r a = r

instance Functor (SFCont r) where
  fmap f (SFCont c) = SFCont $ \ ks -> c (ks . f)   

-- this will be obtained from the definition of the `Monad` instance
instance Applicative (SFCont r) where
  pure  = return
  (<*>) = ap

instance Monad (SFCont r) where
  return a = SFCont $ const . ($ a)
  m >>= f  = SFCont $ \ ks kf -> sfCont m (\ a -> sfCont (f a) ks kf) kf

We can find a systematic way of defining it (and its cousin where failure can take an element of an error type e) by reasoning up to isomorphism and noticing that:

SCont r a -> FCont r a -> r    ~{ unfolding SCont, FCont }
(a -> r) -> r -> r             ~{ r iso (() -> r) }
(a -> r) -> (() -> r) -> r     ~{ uncurrying }
((a -> r), (() -> r)) -> r     ~{ ((a -> r), (b -> r)) iso (Either a b -> r) }
(Either a () -> r) -> r        ~{ Either a () iso Maybe a }
(Maybe a -> r) -> r            ~{ folding MaybeT, Cont }
MaybeT (Cont r) a              

And now, we can leverage existing instances by writing down this iso:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module SFCont where

import Data.Functor.Identity
import Control.Monad
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Cont

newtype SFCont r a = SFCont { sfCont :: SCont r a -> FCont r a -> r }

type SCont r a = a -> r
type FCont r a = r

newtype SFCont' r a = SFCont' { sfCont' :: MaybeT (Cont r) a }
  deriving (Functor, Applicative, Monad)

to :: SFCont r a -> SFCont' r a
to m = SFCont' $ MaybeT $ ContT $ \ k -> Identity $
       sfCont m (runIdentity . k . Just) (runIdentity $ k Nothing)

from :: SFCont' r a -> SFCont r a
from m = SFCont $ \ ks kf -> runIdentity $
         (runContT $ runMaybeT $ sfCont' m) (Identity . maybe kf ks)

instance Monad (SFCont r) where
  return  = from . return
  m >>= f = from $ to m >>= to . f

In the case where the failure continuation could take an element of an error type e, you'd follow the same process and arrive at the stack EitherT e (Cont r) a for your iso.

gallais
  • 11,823
  • 2
  • 30
  • 63