Ezis
Ezis

Reputation: 67

Haskell define return for Monad with function and Either

how do i define a monad for such a datatype in Haskell? It is basically a salsa interpreter. And I cant figure out what should the return look like. It is making me crazy...

newtype Salsa a = Salsa {
    runSalsa :: Context -> Either String (a, Context, Animation)}

instance Monad Salsa where
    return a = Salsa $ ..........

instance Functor Salsa where
    fmap = liftM
instance Applicative Salsa where
    pure = return
    (<*>) = ap

http://ap-e2015.onlineta.org/assignments/assignment-1-salsa-interpreter.html

Upvotes: 0

Views: 188

Answers (3)

Franky
Franky

Reputation: 2376

Help yourself with typed holes: Write an underscore where you are stuck:

instance Monad Salsa where
    return a = Salsa $ _

and the compiler tells you it needs a function here

Found hole ‘_’
  with type: Context -> Either String (a, Context, Animation)

Now you can work your way with

instance Monad Salsa where
    return a = Salsa $ \x -> _

For >>=, do almost the same:

(Salsa s) >>= f = Salsa $ \x -> _ 

and the compiler outputs

Found hole ‘_’ with type: Either String (b, Context, Animation)
Relevant bindings include
  con :: Context
  f :: a -> Salsa b
  s :: Context -> Either String (a, Context, Animation)

So, s is function that requires a Context, but our con supplies one, put it together:

(Salsa s) >>= f = Salsa $ \con -> let s' = s con in _
...
  s' :: Either String (a, Context, Animation)
    (bound at Review.hs:12:43)
  f :: a -> Salsa b (bound at Review.hs:12:19)

So we need that a thing out of s' to supply it to f. Pattern match on s' (while renaming):

(Salsa salsaFunction1) >>= f = Salsa $ \context1 -> 
  let salsaResult1 = salsaFunction1 context1
   in case salsaResult1 of
        Left errorMsg                   -> Left errorMsg
        Right (a, context2, animation1) ->
           let Salsa salsaFunction2 = f a
               salsaResult2         = salsaFunction2 context2
            in _


  salsaFunction2 :: Context -> Either String (b, Context, Animation)
  animation1 :: Animation
  context2 :: Context
  a :: a
  salsaResult1 :: Either String (a, Context, Animation)
  context1 :: Context
  f :: a -> Salsa b 
  salsaFunction1 :: Context -> Either String (a, Context, Animation)

So, we have another salsaFunction2, and an unused context2. You already saw how to put this together: do another case analysis, in the Right case supply context3, the monadic result b and combine both animations to provide the final Either String (b, Context, Animation) that was seen again and again:

Found hole ‘_’ with type: Either String (b, Context, Animation)

Upvotes: 3

amalloy
amalloy

Reputation: 91907

Well, I don't know anything about Salsa, but after reading Guvante's answer foreshadowing the difficulty of implementing >>=, I thought it would be an interesting exercise. And since I don't know what Salsa's Context or Animation types are, I decided to just parameterize over them, which I think worked out fairly well: the type of Context can be totally opaque to us as implementors of >>=, and we just need Animation to be a monoid:

newtype Salsa ctx error anim a = Salsa {
    runSalsa :: ctx -> Either error (a, ctx, anim)
    }

instance Monoid anim => Monad (Salsa ctx error anim) where
    return x = Salsa $ \ctx -> return (x, ctx, mempty)
    (Salsa m) >>= f = Salsa m'
      where m' ctx = m ctx >>= handle
            handle (x, ctx, anims) = let (Salsa f') = f x
                                         merge (a, b, c) = (a, b, mappend anims c)
                                     in merge <$> f' ctx


instance Monoid anim => Functor (Salsa ctx error anim) where
    fmap = liftM
instance Monoid anim => Applicative (Salsa ctx error anim) where
    pure = return
    (<*>) = ap

This as general as I could figure how to make it, and I'm still not totally happy with the implementation of handle: it seems like there must be a better way to get the animation results combined than letting a function and then fmapping it, but I couldn't find anything prettier.

Incidentally I think it would be nicer to have a real data type for this (a, Context, Animation) group rather than just a tuple. Then you could, for example, give it a Functor instance and simplify the implementation of handle, removing the merge function and just writing

mappend anims <$> f' ctx

Upvotes: 1

Guvante
Guvante

Reputation: 19203

You need a function which takes a Context so use a lambda \con ->.

You don't have anything that has failed so far so you can always succeed. Right.

a is provided by the call to return. (a,.

Context is provided by the call to the lambda. con,.

Now you have to decide on the animations to include, I would guess none. []). (Note I don't remember offhand the exact syntax there but I think this is right).

Putting it all together you get:

return a = Salsa $ \con -> Right (a, con, [])

Now comes the complicated case where you have to handle bind (>>=).

Upvotes: 6

Related Questions