10. newtype を使って型クラスのインスタンスを作る (p.260)
型引数が一致しなくて型クラスのインスタンスにできない場合が
ある
Maybe を Functor にするのは簡単
class Functor f where
fmap :: (a -> b) -> f a -> f b
に対して f = Maybe で
instance Functor Maybe where
とし,あとは fmap を実装するだけ
10 / 59
11. タプルを Functor に
Q タプルを Functor にするには?
例えば,fmap が第一要素に対して働くようにしたい
(fmap (+3) (1, 1) が (4, 1) になる)
タプルのままだと型 (a, b) の a を fmap が変更する
ことを表すのは難しい.
A タプルを newtype して,2 つの型引数の順番を入れ
替えることで Functor のインスタンスにできる
newtype Pair b a = Pair { getPair :: (a, b) }
instance Functor (Pair c) where
fmap f (Pair (x, y)) = Pair (f x, y)
newtype で作った型にはパターンマッチも使える取り出したタプ
ルの第一要素 (x) に f を適用してから Pair x y 型に再変換
11 / 59
12. この時 fmap の型は
fmap :: (a -> b) -> Pair c a -> Pair c b
class Functor f where
fmap :: (a -> b) -> f a -> f b
の f = Pair c となる
ghci> getPair $ fmap (*100) (Pair (2, 3))
(200,3)
ghci> getPair $ fmap reverse (Pair ("london calling", 3))
("gnillac nodnol",3)
12 / 59
33. Sum
mappend として*ではなく+を使うのが Sum
ghci> getSum $ Sum 2 `mappend` Sum 9
11
ghci> getSum $ mempty `mappend` Sum 3
3
ghci> getSum . mconcat . map Sum $ [1,2,3]
8
Num a 自身は Monoid のインスタンスではない
33 / 59
34. Any と All(p.271)
Bool もモノイドにする方法が 2 通りある
1. 論理和||をモノイド演算とし False を単位元とする
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)
34 / 59
35. Any を使ってみる
1 つでも True があれば結果は True になる
ghci> getAny $ Any True `mappend` Any False
True
ghci> getAny $ mempty `mappend` Any True
True
ghci> getAny . mconcat . map Any $
[False, False, False, True]
True
ghci> getAny $ mempty `mappend` mempty
False
35 / 59
36. All
2. 論理積&&をモノイド演算とし True を単位元とする
newtype All = All { getAll :: Bool }
instance Monoid All where
mempty = All True
All x `mappend` All y = All (x && y)
36 / 59
37. All を使ってみる
全てが True の場合のみ結果が True になる
ghci> getAll $ mempty `mappend` All True
True
ghci> getAll $ mempty `mappend` All False
False
ghci> getAll . mconcat . map All $ [True, True, True]
True
ghci> getAll . mconcat . map All $ [True, True, False]
False
Monoid を使わなくても [Bool] -> Bool の関数 or や and が
ある
37 / 59
40. Ordering を使う
2 つの文字列を引数にとり Ordering を返す関数
長さを比較した結果を返す
長さが同じ時は EQ ではなく文字列の辞書順比較の結果を
返す
素直な書き方
lengthCompare :: String -> String -> Ordering
lengthCompare x y = let a = length x `compare` length y
b = x `compare` y
in if a == EQ then b else a
40 / 59
43. Maybe モノイド (p.275)
Maybe a も複数の方法でモノイドになれる
1. a がモノイドの時に限り Maybe a も Nothing を単位元として
Just の中身の mappend を使うモノイド
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)
ghci> Nothing `mappend` Just "andy"
Just "andy"
ghci> Just LT `mappend` Nothing
Just LT
ghci> Just (Sum 3) `mappend` Just (Sum 4)
Just (Sum {getSum = 7})
失敗するかもしれない計算の返り値を扱うのに便利 43 / 59
44. First
a がモノイドでなければ使えない mappend を使わないなら任意の
Maybe をモノイドにできる
2. 第一引数が Just なら第二引数を捨てる
newtype First a = First { getFirst :: Maybe a }
deriving (Eq, Ord, Read, Show)
instance Monoid (First a) where
mempty = First Nothing
First (Just x) `mappend` _ = First (Just x)
First Nothing `mappend` x = x
44 / 59
45. First を使ってみる
ghci> getFirst $ First (Just 'a') `mappend`
First (Just 'b')
Just 'a'
ghci> getFirst $ First Nothing `mappend`
First (Just 'b')
Just 'b'
ghci> getFirst $ First (Just 'a') `mappend`
First Nothing
Just 'a'
ghci> getFirst . mconcat . map First $
[Nothing, Just 9, Just 10]
Just 9
45 / 59
46. Last
3. 第 2 引数が Just なら第 1 引数を捨てる
Data.Monoid に Last a も用意されている
ghci> getLast . mconcat . map Last $
[Nothing, Just 9, Just 10]
Just 10
ghci> getLast $ Last (Just "one") `mappend`
Last (Just "two")
Just "two"
46 / 59
47. 余談 単位元が無かったら?
モノイドは主に畳み込みに使われる
Q リストの畳み込みだけなら mempty が無くてもでき
るのでは?
A foldr1 のように空リストの場合にエラーを吐くか,
結果の型が Maybe m になる
空リストでも使える畳み込み mconcat :: [m] -> m にモノイド
は必要十分
47 / 59
48. 12.3 のまとめ
今まで見てきた型にもモノイドがある
newtype で複数のインスタンスを持つ場合も
リスト
ZipList
Num
Sum
Product
Bool
Any
All
Ordering
Maybe
First
Last
48 / 59
51. Foldable を見てみよう
Prelude のものと区別するために別名を付ける
import qualified Data.Foldable as F
ghci> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
ghci> :t F.foldr
F.foldr :: (F.Foldable t) =>
(a -> b -> b) -> b -> t a -> b
t = [] で等価
F.foldr は Foldable なら畳み込みできるので,より一般化
51 / 59
53. 7 章の木構造を Foldable にしてみよう
data Tree a = EmptyTree | Node a (Tree a) (Tree a)
deriving (Show)
を Foldable にすれば畳み込みが可能になる.
ある型コンストラクタを Foldable のインスタンスにするには
foldr か foldMap を実装すれば良い
foldMap 関数の方が簡単
53 / 59
54. foldMap とは
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
a -> m: 中身からモノイドへ変換する関数
t a: Foldable な構造
m: 結果のモノイド値
構造の中身をモノイド値に変換してから畳み込む関数
54 / 59
55. Foldable Tree のインスタンス宣言
instance F.Foldable Tree where
foldMap f EmptyTree = mempty
foldMap f (Node x l r) = F.foldMap f l `mappend`
f x `mappend`
F.foldMap f r
左右の部分木,ノードの値それぞれがモノイドになるので
`mappend`できる.
順番が大事!
55 / 59