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
ghci> fold "Long", "Cat", "Is", "Long"
"LongCatIsLong"
foldr (<>) memptyまたはfoldl (<>) memptyと同じ意味なのだが、「要素ゼロ個の値がある」「foldrでもfoldlでも同じ値になる」ということが保障されるため文法的ノイズが非常に少なくなる。リストに限ればmconcatと同じ意味。
直接モノイドになっていない型でもfoldMapを使えば行ける。
code:haskell
ghci> getAll $ foldMap All True, False, False
False
ghci> getAny $ foldMap Any True, False, 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
ghci> getAny $ foldMap Any True, False, False
True
All
code:haskell
ghci> getAll $ foldMap All True, False, False
False
列
大抵の列はモノイドである。
code:haskell
mconcat 1,2],3,[4,5
1,2,3,4,5
リストのように、結合にコストの掛かる構造だと、パフォーマンス問題が出る場合があるので注意。
文字列
大抵の文字列はモノイドである。
code:haskell
ghci> fold "Long", "Cat", "Is", "Long"
"LongCatIsLong"
StringやTextのように結合にコストの掛かる構造だと(ry
Endo
Endomorphism。 a -> a の関数はモノイドである。
code:haskell
ghci> appEndo (foldMap Endo abs,(*2),(+1),negate) 2
2 -- abs ((*2) ((+1) (negate 2)))
どっちの順番で合成するのか分からなくなりがちだが、リストで書いたとき右にあるものを先に適用する順になるようだ。
これを使うと差分リストを使うためにdlistパッケージをインポートする必要がなくなる。
code:haskell
ghci> dlist = Endo
ghci> dlistToList l = appEndo l []
ghci> dlistToList $ foldMap dlist [(1,2,3++), (4,5++),(6++)]
1,2,3,4,5,6