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
Thanks for the thoughtful comments! You've put me on the right track for
mempty, here's what I've got now:This feels correct, because we're delegating to
memptyfor the value ofa, and we're just returningsunchanged, similar to an identity function. Now I can prove thatmemptyis behaving in isolation:Ok, on to
mappend!I know I'll have two
Mems, each with a function inside, so I can destructure that in the argument:Now, each function has the same signature
s -> (a, s), so I'll need to create a new function that combines the output offandf'into a new tuple. I don't think I can use straight-up function composition because the output of one function can't be used as the input for the next.