newtype
newtype
はある型を値コンストラクタで包んで新しい型を作成する。
newtype
はdata
キーワードの作用ととてもよく似ているが、
- 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)