Search code examples
monadsexistential-typepurescript

Prompt Monad in Purescript


I'm working through the definition of Prompt and its Bind instance described here and was trying to figure out how this would look in Purescript.

I am using Purescript.Exists for the existential type. My defintion is then:

data PromptAskF p r a
  = PromptAskF (p a) (a -> Prompt p r)

type PromptAsk p r = Exists (PromptAskF p r)

data Prompt p r
  = Ask (PromptAsk p r)
  | Answer r 

instance bindPrompt :: Bind (Prompt p) where
  bind (Answer x) k = k x
  bind (Ask ask) k = ???

I am stuck on writing the Ask case in the Bind instance and, in particular, I'm very confused about the types when working with runExists.

How should I write this instance?

Thanks,

Michael


Solution

  • Something like this should do the trick:

    data PromptAskF p r a
      = PromptAskF (p a) (a -> Prompt p r)
    
    type PromptAsk p r = Exists (PromptAskF p r)
    
    mapPA
      :: forall p r r'
       . (forall a. (a -> Prompt p r) -> (a -> Prompt p r'))
      -> PromptAsk p r
      -> PromptAsk p r'
    mapPA f = runExists \(PromptAskF req cont) -> mkExists $ PromptAskF req (f cont)
    
    data Prompt p r
      = Ask (PromptAsk p r)
      | Answer r
    
    instance functorPrompt :: Functor (Prompt p) where
      map f (Answer r) = Answer (f r)
      map f (Ask ask) = Ask $ mapPA (map (map f)) ask
    
    instance applyPrompt :: Apply (Prompt p) where
      apply = ap
    
    instance applicativePrompt :: Applicative (Prompt p) where
      pure = Answer
    
    instance bindPrompt :: Bind (Prompt p) where
      bind (Answer x) k = k x
      bind (Ask ask) k = Ask $ mapPA (\cont ans -> cont ans >>= k) ask
    
    instance monadPrompt :: Monad (Prompt p)
    

    The mapPA function is a convenience for updating the PromptAskF continuation without having to repeatedly runExists / mkExists.