Haskell モノイドの学習

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

newtype

newtypeはある型を値コンストラクタで包んで新しい型を作成する。

newtypedataキーワードの作用ととてもよく似ているが、

  • 1つの値コンストラクタに1つのフィールドのみ定義できる
  • dataより高速に作用する。らしい。
ghci> newtype CharList = CharList { getCharList :: [Char] } deriving (Show)
ghci> :t CharList 
CharList :: [Char] -> CharList
ghci>  CharList "Hello"
CharList {getCharList = "Hello"}

新しい型を定義するのはdata、既存の型に対して型クラスのインスタンスとして扱いたい場合はnewtypeの使用をするという考え方でいいらしい。


モノイド

以下の例でいうと...

ghci > 4 * 1
4
ghci > 9 * 1
9
ghci > [1,2,3] ++ []
[1,2,3]
ghci > [] ++ [0.5, 2.5]
[0.5, 2.5]

*は1という数字を引数に取ることで相手の値を変化させない。

[]も同様に相手の値を変化させない値となる。

こういったを*や++関数の単位元と呼ぶ。

また、以下のように関数を行う順序を変更しても結果が変わらない関数がある。

ghci> (3 * 2* (8 * 5)
240
ghci> 3 * (2 * (8 * 5))
240
ghci> "la" ++ ("di" ++ "ga")
"ladiga"
ghci> ("la" ++ "di"++ "ga"
"ladiga”

この性質を結合的という。

結合的な2つの引数をとる関数と単位元からなる構造をモノイドと呼ぶ。

つまり、*++はモノイドの条件を満たす関数といえる。


モノイド型クラス
class Monoid m where
    mempty :: m
    mappend :: m -> m -> m
    mconcat :: [m] -> m
    mconcat = foldr mappend mempty

memptyは上記でいう単位元となる値を指す。mconcatはデフォルト実装があり、リストに包んだモノイドをmemptyを初期値とし折りたたみながらmappendしていく。


リストのモノイド

リストのモノイド実装

instance Monoid [a] where
    mempty = []
    mappend = (++)

mconcatの例を見ると把握しやすい。

ghci> mconcat [[1,2],[5,6],[8,9]]
[1,2,5,6,8,9]
ghci> mconcat ["Hello", "Haskell", "World"]
"HelloHaskellWorld"


数値のモノイド

数値は関数*+がモノイド則を満たす。

よって、既存の型に値コンストラクタを被せた新しい型(newtype)をモノイドクラス型のインスタンスとして振る舞うような設計にすることで、*+両方のモノイドインスタンスを作成できる。

数値モノイドの実装。

newtype Product a =  Product { getProduct :: a }
    deriving (Eq, Ord, Read, Show, Bounded)

newtype Sum a =  Sum { getSum :: a }
    deriving (Eq, Ord, Read, Show, Bounded)

instance Num a => Monoid (Product a) where
    mempty = Product 1
    Product x `mappend` Product y = Product (x * y)

instance Num a => Monoid (Sum a) where
    mempty = Sum 0
    Sum x `mappend` Sum y = Sum (x + y)
ghci> import Data.Monoid 
ghci> getSum . mconcat . map Sum $ [1,2,3]
6


Boolのモノイド AnyとALL

Anyは単位元をFalseとしたモノイド。どちらかがTrueの場合、Trueを返す。

newtype Any = Any { getAny :: Bool }
    deriving (Eq, Ord, Read, Show, Bounded)

instance Monoid Any where
  mempty = Any False
  Any x `mappend` Any y = Any (x || y)

ALLはTrueを単位元としたモノイド。両方Trueの場合、Trueを返す。

instance Monoid All where
  mempty = All True
  All x `mappend` All y = All (x && y)”


Orderingモノイド

EQが単位元。mappendは単位元のEQ以外、左辺が返される。つまりEQの時のみ右辺のOrderingを返す式が実行されることになる。

instance Monoid Ordering where
  mempty = EQ
  LT `mappend` _ = LT
  EQ `mappend` y = y
  GT `mappend` _ = GT”


Maybeモノイド

Nothingが単位元。

instance Monoid a => Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m = m
  m `mappend` Nothing = m
  Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)