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
mempty
for the value ofa
, and we're just returnings
unchanged, similar to an identity function. Now I can prove thatmempty
is behaving in isolation:Ok, on to
mappend
!I know I'll have two
Mem
s, 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 off
andf'
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.