18. newtype Pair b a = Pair { getPair :: (a, b) }
-- パターンマッチを使ってPair型から中身のタプルを取り出し、
-- fをタプルの第一要素に適用し、
-- Pair値コンストラクタを使ってタプルをPair b a型に再変換
instance Functor (Pair c) where
fmap f (Pair (x, y)) = Pair (f x, y)
-- fmapの型(a -> b) -> f a -> f bは
-- Pair b a型の場合は
-- (a -> b) -> Pair c a -> Pair c b
:type fmap
:type fmap (*100) Pair
print (getPair $ fmap (*100) (Pair (2, 3)))
print (getPair $ fmap reverse (Pair ("london calling", 3)))
fmap :: forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (*100) Pair :: forall b a. Num (Pair b a) => (a, b) -> Pair b a
(200,3)
("gnillac nodnol",3)
40. -- Monoid型クラスの定義
-- (Data.Monoidモジュールにて定義されている)
--
-- mは型クラスの関数定義中で型引数を取らないため
-- FunctorやApplicativeとは違って具体型となる
--
-- class Functor f where
-- fmap :: (a -> b) -> f a -> f b
--
class Monoid m where
mempty :: m
mappend :: m -> m -> m
mconcat :: [m] -> m
mconcat = foldr mappend mempty
50. import Data.Monoid
-- SumとProductのnewtype宣言とインスタンス宣言
-- 実際にはData.Monoidで定義
{-
-- netypeの定義
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)
-}
53. import Data.Monoid
-- AnyとAllのnewtype宣言とインスタンス宣言
-- 実際にはData.Monoidで定義
{-
-- netypeの定義
newtype Any = Any { getAny :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
newtype All = All { getAll :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
-- インスタンス宣言の例
instance Monoid Any where
mempty = Any False
Any x `mappend` Any y = Any (x || y)
instance Monoid All where
mempty = All True
All x `mappend` All y = All (x && y)
-}
54. -- 使ってみる
print "Any example"
print (getAny $ Any True `mappend` Any False)
print (getAny $ mempty `mappend` Any True)
print (getAny . mconcat . map Any $ [False, False, False, True])
print (getAny $ mempty `mappend` mempty)
print "All example"
print (getAll $ mempty `mappend` All True)
print (getAll $ mempty `mappend` All False)
print (getAll . mconcat . map All $ [True, True, True])
print (getAll . mconcat . map All $ [True, True, False])
{-
こんなふうに包んだり解いたりするのは面倒なので普通はandやorの関数と[Bool]を使う
-}
"Any example"
True
True
True
False
"All example"
True
False
True
False
62. -- 長さの比較結果をa, 辞書順の比較結果をbとし、長さが等しければbを返す
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
-- Orderingはモノイドであるという知識を使えばもっとシンプルに書ける
import Data.Monoid
lengthCompare :: String -> String -> Ordering
lengthCompare x y = (length x `compare` length y) `mappend` (x `compare` y)
67. import Data.Monoid
-- Maybe a型に対するインスタンス定義、aがMonoidである制約を付けている
-- 実際にはData.Monoidで宣言されている
{-
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)
-}
68. -- 使ってみる
print (Nothing `mappend` Just "andy")
print (Just LT `mappend` Nothing)
print (Just (Sum 3) `mappend` Just (Sum 4))
Just "andy"
Just LT
Just (Sum {getSum = 7})
70. import Data.Monoid
-- Firstのnewtypeとインスタンス宣言
-- 実際にはData.Monoidで定義
{-
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
-}
71. -- 試す
print (getFirst $ First (Just 'a') `mappend` First (Just 'b'))
print (getFirst $ First Nothing `mappend` First (Just 'b'))
print (getFirst $ First (Just 'a') `mappend` First Nothing)
-- リストの中にJust値があるかも調べられる
print (getFirst . mconcat . map First $ [Nothing, Just 9, Just 10])
Just 'a'
Just 'b'
Just 'a'
Just 9
-- 反対の定義(第二引数優先)のLast a型もData.Monoidに存在
import Data.Monoid
print (getLast . mconcat . map Last $ [Nothing, Just 9, Just 10])
print (getLast $ Last (Just "one") `mappend` Last (Just "two"))
Just 10
Just "two"
73. import qualified Data.Foldable as F
print "type of foldr"
:type foldr
-- Data.Foldableのfoldrは畳み込みができる型ならリストに限らずなんでも畳み込める
print "type of F.foldr"
:type F.foldr
74. -- 実例
print (foldr (*) 1 [1,2,3])
print (F.foldr (*) 1 [1,2,3])
-- Maybeも可能
print (F.foldl (+) 2 (Just 9))
print (F.foldr (||) False (Just True))
Use product
Found: foldr (*) 1 Why Not: product
"type of foldr"
foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b
"type of F.foldr"
F.foldr :: forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b
6
6
11
True
76. import qualified Data.Foldable as F
import Data.Monoid
-- 木構造の定義
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
:type F.foldMap
-- TreeをFoldableのインスタンスにする
-- (a -> m)の結合関数は引数で与えるので
-- どのように結果を結合するかを実装では指定する
-- 結合の順番が大切なので以下が唯一の実装ではない
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
77. -- 使ってみる
testTree = Node 5
(Node 3
(Node 1 EmptyTree EmptyTree)
(Node 6 EmptyTree EmptyTree)
)
(Node 9
(Node 8 EmptyTree EmptyTree)
(Node 10 EmptyTree EmptyTree)
)
print (F.foldl (+) 0 testTree)
print (F.foldl (*) 1 testTree)
-- 木の中に3に等しい数があるか調べる
print (getAny $ F.foldMap (x -> Any $ x == 3) testTree)
-- 15より大きい数があるか
print (getAny $ F.foldMap (x -> Any $ x > 15) testTree)
-- Treeをリストに変換
print (F.foldMap (x -> [x]) testTree)
Use :
Found: x -> [x] Why Not: (: [])
F.foldMap :: forall (t :: * -> *) a m. (Monoid m, Foldable t) => (a -> m) -> t a -> m
42
64800
True
False
[1,3,6,5,8,9,10]