This gist and its comments are a way of working through an exercise problem found in Chapter 15 of the Haskell Book.
Here's the code from the exercise. Our job is to implement the mempty
and mappend
functions, which are presented here as undefined
:
module Chap15Ex where
import Data.Monoid
newtype Mem s a =
Mem {
runMem :: s -> (a, s)
}
instance Monoid a => Monoid (Mem s a) where
mempty = undefined
mappend = undefined
f' :: Mem Integer String
f' = Mem $ \s -> ("hi", s + 1)
main = do
let rmzero = runMem mempty 0
rmleft = runMem (f' <> mempty) 0
rmright = runMem (mempty <> f') 0
print $ rmleft -- ("hi, 1)
print $ rmright -- ("hi", 1)
print $ (rmzero :: (String, Int)) -- ("", 0)
print $ rmleft == runMem f' 0 -- True
print $ rmright == runMem f' 0 -- True
I didn't realize you could destructure in a normal assignment like that - very cool!
I'm getting very, very close now πI've figured it out! ππ¦ Rubber ducking is awesome - I started typing this message to organize my thoughts before taking a break for the day, but as I was typing it out I realized how I could solve it!
We have to accomplish the following:
mappend
will ultimately return;mappend
;a
values and our twos
values;mappend
botha
values, since they're both guaranteed to be monoidal (yay, laws!)s
values*.If we can ignore the tuple and make the function return just one value, then we can compose things together!
All that's left is to take the new function we wrote, and wrap it up in a nice
Mem
package ποΈDrumroll please... π₯ π₯ π₯
The solution (here be spoilers)
I used
fst
andsnd
to handle the tuples:For what it's worth, I started with this parenthetical monstrosity, but it really helped me work through what I needed to do:
Now that I've figured it out, I'd love to hear if there's a more idiomatic way of writing this instance, I'm curious about other implementations!
Thanks again for your help, @expede! You gave me the perfect level of hints to lead me towards the solution without giving too much away π π