MonoidからMonadを作る
import Data.Monoid
data Stamp m a = Stamp a m
instance Monoid m => Monad (Stamp m) where
return a = Stamp a mempty
(Stamp a m) >>= f = let Stamp b n = f a in Stamp b (m <> n)
stamp :: Monoid m => m -> Stamp m ()
stamp = Stamp ()
runStamp :: Stamp m a -> (a, m)
runStamp (Stamp a m) = (a, m)
任意のモノイドからモナドが作れると聞いて書いてみた。monoidal stamping monadと言うそうです。
: 追記
@solo_rab というか、Writerモナドが monoidal stamping monad そのものです。
— Masahiro Sakai (@masahiro_sakai) December 22, 2014
ありがたいことに訂正をいただいた。
つまりこのStamp
はWriter
そのものであるらしい。実際、mtl
でのWriter
の定義1を見ると
newtype Writer w a = Writer (a, w)
instance (Monoid w) => Monad (Writer w) where
...
であり、Monad
の実装も一致している。
例
自由モノイド[a]
はWriter
monoidal stamping monadはWriter
そのもの
>>> runStamp $ do { stamp "foo" ; (do { stamp "bar" ; return "baz" }) >>= stamp }
((), "foobarbaz")
>>> runWriter $ do { tell "foo" ; (do { tell "bar" ; return "baz" }) >>= tell }
((), "foobarbaz")
左自明モノイドFirst a
は単一代入
>>> runStamp $ do { stamp (First (Just 'a')) ; stamp (First (Just 'b')) ; return 'c' }
('c', First (Just 'a'))
右自明モノイドLast a
は破壊的代入
>>> runStamp $ do { stamp (Last (Just 'a')) ; stamp (Last (Just 'b')) ; return 'c' }
('c', First (Just 'b'))
自明モノイド()
はIdentity
>>> runStamp $ do { stamp () ; stamp () ; return 42 }
(42, ())
面白いですね。
詳しく
モノイド$(M,\cdot,e)$に対し
- 関手 $X \rightarrowtail X \times M$
- 単位元 $\eta(x) := x \times e$
- 乗法 $\mu((x \times n) \times m) = x \times (m \cdot n)$
とすると、これはモナド(monoidal stamping monad)になる。
同様にcomonoidからcomonadが作れるらしい。
参考
-
ここでは
WriterT
を使わない古いものを用いた ↩