すごいHaskell読書会
-
Upload
kosuke-usami -
Category
Software
-
view
39 -
download
0
Transcript of すごいHaskell読書会
すごいHaskell読書会in 大阪 2週目 #8
宇佐見 公輔
第7章型や型クラスを自分で作ろう
(後編)
前編のおさらい4 新しいデータ型を定義する4 レコード構文4 型引数4 インスタンスの自動導出4 型シノニム
(前編のおさらい)新しいデータ型を定義するdata 型名 = 値コンストラクタdata Bool = False | Truedata Shape = Circle Float Float Float | Rectangle Float Float Float Floatdata Point = Point Float Float
(前編のおさらい)レコード構文data Person = Person { firstName :: String , lastName :: String , age :: Int , height :: Float , phoneNumber :: String , flavor :: String }
(前編のおさらい)型引数data 型コンストラクタ = 値コンストラクタ (型名 型引数)data Maybe a = Nothing | Just adata Either a b = Left a | Right b
-- 3次元ベクトルの例data Vector a = Vector a a a
(前編のおさらい)インスタンスの自動導出data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Ord, Show, Read, Bounded, Enum)
-- 型クラスの実装が補われ、以下が可能になるghci> Saturday == SundayFalseghci> show WednesdayWednesday
(前編のおさらい)型シノニムtype String = [Char]
type Name = Stringtype PhoneNumber = Stringtype PhoneBook = [(Name, PhoneNumber)]
-- 以下の例では AssocList は型コンストラクタtype AssocList k v = [(k, v)]
後編の内容4 再帰的なデータ構造4 型クラス中級講座4 Yes と No の型クラス4 Functor 型クラス4 型を司るもの、種類
再帰的なデータ型
再帰的なデータ型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:[]
例:独自リスト型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)
独自リスト型のリスト表現4 先の独自定義の Cons は標準の : にあたるものEmpty -- [] にあたる5 `Cons` Empty -- 5:[] にあたる4 `Cons` (5 `Cons` Empty) -- 4:5:[] にあたる3 `Cons` (4 `Cons` (5 `Cons` Empty)) -- 3:4:5:[] にあたる
独自リスト型の改善4 値コンストラクタを中置関数に(: で始めること)4 結合の優先度を定義(infixl や infixr を使う)infixr 5 :-:data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
改善結果4 リストをこう書けるようになった5 :-: Empty -- 5 `Cons` Empty4 :-: 5 :-: Empty -- 4 `Cons` (5 `Cons` Empty)3 :-: 4 :-: 5 :-: Empty -- 3 `Cons` (4 `Cons` (5 `Cons` Empty))
例:独自リスト型の結合infixr 5 ^++(^++) :: List a -> List a -> List aEmpty ^++ ys = ys(x :-: xs) ^++ ys = x :-: (xs ^++ ys)
4 実はパターンマッチとは、値コンストラクタのマッチ4 標準の : も値コンストラクタ
別の例:二分探索木data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
4 二分探索木:各要素について、4 左部分木の全要素がその要素より小さい4 右部分木の全要素がその要素より大きい
木に要素を追加する関数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)
木の生成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
木に要素が属するか調べる関数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
型クラス中級講座
型クラス中級講座4 型クラスの復習4 ある型 T がある型クラス C のインスタンス
= 型 T に対して型クラス C が定義する関数を使える4 型クラスを自分で作るには?
Eq 型クラスの宣言class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)
4 a は型引数で、Eq のインスタンスとなる型
Eq 型クラスのインスタンス宣言data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where Red == Red = True Yellow == Yellow = True Green == Green = True _ == _ = False
4 注意:通常は deriving で自動導出する
最小完全定義 x == y = not (x /= y) x /= y = not (x == y)
4 Eq のインスタンスは == か /= のどちらかを定義する必要がある
4 片方を定義(デフォルト実装を上書き)すれば、もう片方も定義される
Show 型クラスのインスタンス宣言4 自動導出での定義でなく、自前で定義したい場合にinstance Show TrafficLight where show Red = "Red light" show Yellow = "Yellow light" show Green = "Green light"
型クラス宣言に型クラス制約をつける4 Num 型クラスは Eq 型クラスのサブクラスclass (Eq a) => Num a where ...
Maybe を Eq のインスタンスにする?class Eq a where (==) :: a -> a -> Bool
4 型引数 a は具体型でなくてはダメ(関数定義にあわない)4 Maybe は具体型でなく多相型(型コンストラクタ)4 じゃあ具体型 Maybe Char を Eq のインスタンスにする?4 それでは Maybe Int は? ・・・きりがない
多相型を型クラスのインスタンスにする4 インスタンス宣言に型引数をつけるinstance Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False
4 注意:この宣言は不十分(次ページ参照)
インスタンス宣言に型クラス制約をつける4 型 m が Eq 型クラスでないとダメ4 型クラス制約をつける
instance (Eq m) => Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False
再度整理してみる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
型クラスの情報4 ghci で :info Maybe などとすれば情報が得られる
Yes と No の型クラス
Yes と No の型クラス4 お題:型クラスを作ってみる
Yes と No の型クラス4 Haskell では真理値が必要な箇所では厳密に Bool 型を使う4 他の言語ではそうでないものもある:JavaScript の例4 if (0)
4 if ("")
4 if (false)
4 類似のことを独自の型クラスで実現してみる
型クラス宣言とインスタンス宣言class YesNo a where yesno :: a -> Bool
instance YesNo Int where yesno 0 = False yesno _ = True
instance YesNo [a] where yesno [] = False yesno _ = True
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
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!"
Functor 型クラス
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 を適用した型
リストの map-- リストの map
map :: (a -> b) -> [a] -> [b]
-- Functor の fmap
fmap :: (a -> b) -> f a -> f b
4 map はリスト限定で動作する fmap だといえる
リストの Functor インスタンス宣言instance Functor [] where fmap = map
4 ここで、[] は型コンストラクタであることに注意4 [Int] [String] などが具体型
Maybe の Functor インスタンス宣言instance Functor Maybe where fmap f (Just x) = Just (f x) fmap f Nothing = Nothing
4 Maybe は型コンストラクタ
二分探索木の 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 注意:任意の関数を適用した後は二分探索木の性質を保たない
Either の場合は?4 Either は型引数を 2 つとる型コンストラクタ4 Either a b が具体型
4 1 つだけ部分適用すると Functor にできる4 Either a は型引数を 1 つとる型コンストラクタ
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 側には適用できない
Data.Map の場合は?(練習問題)4 Map k v は k 型がキー、v 型が値のデータ構造4 fmap で適用する関数は v -> v' 型4 問題:どのように Functor のインスタンスになるか?4 MyFunctor 型クラス宣言を書く4 Map の MyFunctor インスタンス宣言を書く4 Map に myfmap で関数を適用する
Functor について
より深い内容は後の章で
型を司るもの、種類
型を司るもの、種類4 型の種類(kind)4 GHCi の :k コマンドで型の種類を調べる
型の種類ghci> :k IntInt :: *ghci> :k MaybeMaybe :: * -> *ghci> :k Maybe IntMaybe Int :: *
4 * は具体型を表す記号4 Maybe は具体型を引数にとって具体型を返す
値、型、種類4 値のラベルが型4 型のラベルが種類4 GHCi の :t コマンドは値の型を調べる4 GHCi の :k コマンドは型の種類を調べる
型コンストラクタへの部分適用4 型コンストラクタがカリー化されていて部分適用できるghci> :k EitherEither :: * -> * -> *ghci> :k Either StringEither String :: * -> *
Functor になれる型の種類class Functor f where fmap :: (a -> b) -> f a -> f b
4 f a f b の種類は *(関数の型宣言に使われているため)4 f の種類は * -> * でなくてはならない
第7章後編おわり4 再帰的なデータ構造4 型クラス中級講座4 Yes と No の型クラス4 Functor 型クラス4 型を司るもの、種類