すごいHaskell読書会

55
すごいHaskell読書会 in 大阪 2週目 #8 宇佐見 公輔

Transcript of すごいHaskell読書会

Page 1: すごいHaskell読書会

すごいHaskell読書会in 大阪 2週目 #8

宇佐見 公輔

Page 2: すごいHaskell読書会

第7章型や型クラスを自分で作ろう

(後編)

Page 3: すごいHaskell読書会

前編のおさらい4 新しいデータ型を定義する4 レコード構文4 型引数4 インスタンスの自動導出4 型シノニム

Page 4: すごいHaskell読書会

(前編のおさらい)新しいデータ型を定義するdata 型名 = 値コンストラクタdata Bool = False | Truedata Shape = Circle Float Float Float | Rectangle Float Float Float Floatdata Point = Point Float Float

Page 5: すごいHaskell読書会

(前編のおさらい)レコード構文data Person = Person { firstName :: String , lastName :: String , age :: Int , height :: Float , phoneNumber :: String , flavor :: String }

Page 6: すごいHaskell読書会

(前編のおさらい)型引数data 型コンストラクタ = 値コンストラクタ (型名 型引数)data Maybe a = Nothing | Just adata Either a b = Left a | Right b

-- 3次元ベクトルの例data Vector a = Vector a a a

Page 7: すごいHaskell読書会

(前編のおさらい)インスタンスの自動導出data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Ord, Show, Read, Bounded, Enum)

-- 型クラスの実装が補われ、以下が可能になるghci> Saturday == SundayFalseghci> show WednesdayWednesday

Page 8: すごいHaskell読書会

(前編のおさらい)型シノニムtype String = [Char]

type Name = Stringtype PhoneNumber = Stringtype PhoneBook = [(Name, PhoneNumber)]

-- 以下の例では AssocList は型コンストラクタtype AssocList k v = [(k, v)]

Page 9: すごいHaskell読書会

後編の内容4 再帰的なデータ構造4 型クラス中級講座4 Yes と No の型クラス4 Functor 型クラス4 型を司るもの、種類

Page 10: すごいHaskell読書会

再帰的なデータ型

Page 11: すごいHaskell読書会

再帰的なデータ型4 値コンストラクタのフィールドに自分自身の型を持たせる4 例えば、リストは再帰的なデータ型4 [3,4,5,6] は 3:[4,5,6] である4 3:4:[5,6] でもある4 3:4:5:[6]

4 3:4:5:6:[]

Page 12: すごいHaskell読書会

例:独自リスト型data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)

-- レコード構文の場合data List a = Empty | Cons { listhead :: a , listtail :: List a } deriving (Show, Read, Eq, Ord)

Page 13: すごいHaskell読書会

独自リスト型のリスト表現4 先の独自定義の Cons は標準の : にあたるものEmpty -- [] にあたる5 `Cons` Empty -- 5:[] にあたる4 `Cons` (5 `Cons` Empty) -- 4:5:[] にあたる3 `Cons` (4 `Cons` (5 `Cons` Empty)) -- 3:4:5:[] にあたる

Page 14: すごいHaskell読書会

独自リスト型の改善4 値コンストラクタを中置関数に(: で始めること)4 結合の優先度を定義(infixl や infixr を使う)infixr 5 :-:data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)

Page 15: すごいHaskell読書会

改善結果4 リストをこう書けるようになった5 :-: Empty -- 5 `Cons` Empty4 :-: 5 :-: Empty -- 4 `Cons` (5 `Cons` Empty)3 :-: 4 :-: 5 :-: Empty -- 3 `Cons` (4 `Cons` (5 `Cons` Empty))

Page 16: すごいHaskell読書会

例:独自リスト型の結合infixr 5 ^++(^++) :: List a -> List a -> List aEmpty ^++ ys = ys(x :-: xs) ^++ ys = x :-: (xs ^++ ys)

4 実はパターンマッチとは、値コンストラクタのマッチ4 標準の : も値コンストラクタ

Page 17: すごいHaskell読書会

別の例:二分探索木data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)

4 二分探索木:各要素について、4 左部分木の全要素がその要素より小さい4 右部分木の全要素がその要素より大きい

Page 18: すごいHaskell読書会

木に要素を追加する関数4 与えられた木の直接更新は不可、新しい木を作って返すsingleton :: a -> Tree asingleton x = Node x EmptyTree EmptyTree

treeInsert :: (Ord a) => a -> Tree a -> Tree atreeInsert x EmptyTree = singleton xtreeInsert x (Node y left right) | x == y = Node y left right -- 同じなら挿入しない | x < y = Node y (treeInsert x left) right | x > y = Node y left (treeInsert x right)

Page 19: すごいHaskell読書会

木の生成ghci> let nums = [8,6,4,1,7,3,5]ghci> let numsTree = foldr treeInsert EmptyTree nums

4 tree1 = treeInsert 5 EmptyTree

4 tree2 = treeInsert 3 tree1

4 tree3 = treeInsert 7 tree2

4 ...

4 numsTree = treeInsert 8 tree7

Page 20: すごいHaskell読書会

木に要素が属するか調べる関数treeElem :: (Ord a) => a -> Tree a -> BooltreeElem x EmptyTree = FalsetreeElem x (Node y left right) | x == y = True | x < y = treeElem x left | x > y = treeElem x right

Page 21: すごいHaskell読書会

型クラス中級講座

Page 22: すごいHaskell読書会

型クラス中級講座4 型クラスの復習4 ある型 T がある型クラス C のインスタンス

= 型 T に対して型クラス C が定義する関数を使える4 型クラスを自分で作るには?

Page 23: すごいHaskell読書会

Eq 型クラスの宣言class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)

4 a は型引数で、Eq のインスタンスとなる型

Page 24: すごいHaskell読書会

Eq 型クラスのインスタンス宣言data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where Red == Red = True Yellow == Yellow = True Green == Green = True _ == _ = False

4 注意:通常は deriving で自動導出する

Page 25: すごいHaskell読書会

最小完全定義 x == y = not (x /= y) x /= y = not (x == y)

4 Eq のインスタンスは == か /= のどちらかを定義する必要がある

4 片方を定義(デフォルト実装を上書き)すれば、もう片方も定義される

Page 26: すごいHaskell読書会

Show 型クラスのインスタンス宣言4 自動導出での定義でなく、自前で定義したい場合にinstance Show TrafficLight where show Red = "Red light" show Yellow = "Yellow light" show Green = "Green light"

Page 27: すごいHaskell読書会

型クラス宣言に型クラス制約をつける4 Num 型クラスは Eq 型クラスのサブクラスclass (Eq a) => Num a where ...

Page 28: すごいHaskell読書会

Maybe を Eq のインスタンスにする?class Eq a where (==) :: a -> a -> Bool

4 型引数 a は具体型でなくてはダメ(関数定義にあわない)4 Maybe は具体型でなく多相型(型コンストラクタ)4 じゃあ具体型 Maybe Char を Eq のインスタンスにする?4 それでは Maybe Int は? ・・・きりがない

Page 29: すごいHaskell読書会

多相型を型クラスのインスタンスにする4 インスタンス宣言に型引数をつけるinstance Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False

4 注意:この宣言は不十分(次ページ参照)

Page 30: すごいHaskell読書会

インスタンス宣言に型クラス制約をつける4 型 m が Eq 型クラスでないとダメ4 型クラス制約をつける

instance (Eq m) => Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False

Page 31: すごいHaskell読書会

再度整理してみる4 Eq 型クラス宣言では、a が具体型でないとダメ4 (==) :: a -> a -> Bool と使われるから

4 a に Maybe は渡せない4 (==) :: Maybe -> Maybe -> Bool はダメ

4 a に Maybe m なら渡せる4 (==) :: (Eq m) => Maybe m -> Maybe m -> Bool

Page 32: すごいHaskell読書会

型クラスの情報4 ghci で :info Maybe などとすれば情報が得られる

Page 33: すごいHaskell読書会

Yes と No の型クラス

Page 34: すごいHaskell読書会

Yes と No の型クラス4 お題:型クラスを作ってみる

Page 35: すごいHaskell読書会

Yes と No の型クラス4 Haskell では真理値が必要な箇所では厳密に Bool 型を使う4 他の言語ではそうでないものもある:JavaScript の例4 if (0)

4 if ("")

4 if (false)

4 類似のことを独自の型クラスで実現してみる

Page 36: すごいHaskell読書会

型クラス宣言とインスタンス宣言class YesNo a where yesno :: a -> Bool

instance YesNo Int where yesno 0 = False yesno _ = True

instance YesNo [a] where yesno [] = False yesno _ = True

Page 37: すごいHaskell読書会

instance YesNo Bool where yesno = id -- そのまま返す

instance YesNo (Maybe a) where yesno Nothing = False yesno (Just _) = True

instance YesNo (Tree a) where yesno EmptyTree = False yesno _ = True

instance YesNo TrafficLight where yesno Red = False yesno _ = True

Page 38: すごいHaskell読書会

if の代替yesnoIf :: (YesNo y) => y -> a -> a -> ayesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult

ghci> yesnoIf [] "YEAH!" "NO!""NO!"ghci> yesnoIf [2,3,4] "YEAH!" "NO!""YEAH!"

Page 39: すごいHaskell読書会

Functor 型クラス

Page 40: すごいHaskell読書会

Functor 型クラスclass Functor f where fmap :: (a -> b) -> f a -> f b

4 この f は具体型ではなく、型コンストラクタ4 引数1 : 型 a から型 b への関数型4 引数2 : 型コンストラクタ f に型引数 a を適用した型4 戻り値 : 型コンストラクタ f に型引数 b を適用した型

Page 41: すごいHaskell読書会

リストの map-- リストの map

map :: (a -> b) -> [a] -> [b]

-- Functor の fmap

fmap :: (a -> b) -> f a -> f b

4 map はリスト限定で動作する fmap だといえる

Page 42: すごいHaskell読書会

リストの Functor インスタンス宣言instance Functor [] where fmap = map

4 ここで、[] は型コンストラクタであることに注意4 [Int] [String] などが具体型

Page 43: すごいHaskell読書会

Maybe の Functor インスタンス宣言instance Functor Maybe where fmap f (Just x) = Just (f x) fmap f Nothing = Nothing

4 Maybe は型コンストラクタ

Page 44: すごいHaskell読書会

二分探索木の Functor インスタンス宣言instance Functor Tree where fmap f EmptyTree = EmptyTree fmap f (Node x left right) = Node (f x) (fmap f left) (fmap f right)

4 Tree は型コンストラクタ(Tree a が具体型)4 注意:任意の関数を適用した後は二分探索木の性質を保たない

Page 45: すごいHaskell読書会

Either の場合は?4 Either は型引数を 2 つとる型コンストラクタ4 Either a b が具体型

4 1 つだけ部分適用すると Functor にできる4 Either a は型引数を 1 つとる型コンストラクタ

Page 46: すごいHaskell読書会

Either の Functor インスタンス宣言instance Functor (Either a) where fmap f (Right x) = Right (f x) fmap f (Left x) = Left x

4 data Either a b = Left a | Right b

4 Left と Right で型が違うことに注意4 fmap で適用する関数 f は b -> c 型4 Right 側には適用できるが、Left 側には適用できない

Page 47: すごいHaskell読書会

Data.Map の場合は?(練習問題)4 Map k v は k 型がキー、v 型が値のデータ構造4 fmap で適用する関数は v -> v' 型4 問題:どのように Functor のインスタンスになるか?4 MyFunctor 型クラス宣言を書く4 Map の MyFunctor インスタンス宣言を書く4 Map に myfmap で関数を適用する

Page 48: すごいHaskell読書会

Functor について

より深い内容は後の章で

Page 49: すごいHaskell読書会

型を司るもの、種類

Page 50: すごいHaskell読書会

型を司るもの、種類4 型の種類(kind)4 GHCi の :k コマンドで型の種類を調べる

Page 51: すごいHaskell読書会

型の種類ghci> :k IntInt :: *ghci> :k MaybeMaybe :: * -> *ghci> :k Maybe IntMaybe Int :: *

4 * は具体型を表す記号4 Maybe は具体型を引数にとって具体型を返す

Page 52: すごいHaskell読書会

値、型、種類4 値のラベルが型4 型のラベルが種類4 GHCi の :t コマンドは値の型を調べる4 GHCi の :k コマンドは型の種類を調べる

Page 53: すごいHaskell読書会

型コンストラクタへの部分適用4 型コンストラクタがカリー化されていて部分適用できるghci> :k EitherEither :: * -> * -> *ghci> :k Either StringEither String :: * -> *

Page 54: すごいHaskell読書会

Functor になれる型の種類class Functor f where fmap :: (a -> b) -> f a -> f b

4 f a f b の種類は *(関数の型宣言に使われているため)4 f の種類は * -> * でなくてはならない

Page 55: すごいHaskell読書会

第7章後編おわり4 再帰的なデータ構造4 型クラス中級講座4 Yes と No の型クラス4 Functor 型クラス4 型を司るもの、種類