Reputation: 67
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
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
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
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