Monoid
厳密に言うと、掛け算の重要な性質である結合法則 (a * b) * c = a * (b * c)を満たし、さらに a * 1 = 1 * a = a となる1のような値(単位元)を持つ型。単位元を持たない場合はSemigroup。 code:haskell
class Semigroup a where
(<>) :: a -> a -> a
class Semigroup a => Monoid a where
mempty :: a
-- 非必須メンバは略
該当するものは非常に多く、たとえば整数や実数は足し算、掛け算どちらもモノイドとなるし、Boolは論理和も論理積もモノイドである。このため、IntやBoolは直接Monoidにはなっておらず、newtypeでラップしたSumやAnyのMonoidインスタンスを使用する事になる。
モノイドの便利な使い方
fold, foldMap
code:haskell
ghci> import Data.Foldable
"LongCatIsLong"
foldr (<>) memptyまたはfoldl (<>) memptyと同じ意味なのだが、「要素ゼロ個の値がある」「foldrでもfoldlでも同じ値になる」ということが保障されるため文法的ノイズが非常に少なくなる。リストに限ればmconcatと同じ意味。
直接モノイドになっていない型でもfoldMapを使えば行ける。
code:haskell
False
True
Writer
任意のモノイドはWriterを被せるとモナドになるので、do文で書ける。
code:haskell
print $ execWriter $ do
tell "Long"
tell "Cat"
tell "Is"
tell "Long"
リストで書いてmconcatでつなげるケースの方が多いか。
Writerはパフォーマンスの問題があるので、新しめのtransformersパッケージに入っているCPS版を使おう。Strictなら大丈夫、ではない。 主なモノイド
Sum
code:haskell
ghci> getSum $ foldMap Sum 1..5 15
Product
code:haskell
ghci> getProduct $ foldMap Product 1..5 120
Any
code:haskell
True
All
code:haskell
False
列
code:haskell
リストのように、結合にコストの掛かる構造だと、パフォーマンス問題が出る場合があるので注意。
文字列
code:haskell
"LongCatIsLong"
StringやTextのように結合にコストの掛かる構造だと(ry
Endo
Endomorphism。 a -> a の関数はモノイドである。
code:haskell
2 -- abs ((*2) ((+1) (negate 2)))
どっちの順番で合成するのか分からなくなりがちだが、リストで書いたとき右にあるものを先に適用する順になるようだ。
code:haskell
ghci> dlist = Endo
ghci> dlistToList l = appEndo l []
ghci> dlistToList $ foldMap dlist [(1,2,3++), (4,5++),(6++)]