Haskell勉強会2 in ie

83
Haskell勉強会2 in ie 2015/05/04 @maeken2010

Transcript of Haskell勉強会2 in ie

Haskell勉強会2 in ie2015/05/04 @maeken2010

勉強会について

• 前回のHaskell勉強会 in ieの続きのようなもの

• 「すごいhaskell楽しく学ぼう」本を元にしています

• スライド内のコード詰め合わせを下記に公開していますhttps://gist.github.com/maeken2010/8e70e07435137702ac3c

お詫びがあります

今日の勉強会内訳

今日の勉強会内訳

型勉強会 in ie2015/05/04 @maeken2010

目的

• Haskellの型とは

• 型の定義

• 型クラスの定義

• 型インスタンス(少しFunctor)

型おさらい

• 型の種類

• Int,Float,Bool,Char,String,[Int]…

• 型は「値の集合」

• Int,Integer,Float,Char,String

• [Int],[Char],[a]

• (a,b),a->a,Maybe a

• とかとか

型クラスおさらい

• 型クラス

• (==) :: Eq a => a -> a -> Bool

• 型クラスは「型の振る舞いを定義」

• Eqクラス…等価評価

• Ordクラス…大小比較

• Showクラス…文字列表現

• Numクラス…数

• とかとか

型定義

型定義

• dataキーワード

• data Bool = False | True

• data Int = -2147483648 | .. | -1 | 0 | 1 | 2 | .. |2147483647

※実際は違います

データ型

• ex)長方形と円の図形の型を定義

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

データ型

• ex)長方形と円の図形の型を定義

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

データ型

• Shapeが「型」

• Circle,Rectangleが「値コンストラクタ」

• Circle Float Float Floatが「値」

データ型

• 値コンストラクタは普通の関数

ghci> :t Circle Circle :: Float -> Float -> Float -> Shape

値を使う

• 値を作り出す

ghci> let maru = Circle 1.0 4.0 9.0 ghci> maru Circle 1.0 4.0 9.0

値を使う

• 面積を返す関数

area :: Shape -> Float area (Circle _ _ r) = pi*r^2 area (Rectangle x1 y1 x2 y2) = (abs $ x2-x1)*(abs $ y2-y1)

ghci> area maru 254.46901

deriving

• 型を表示したい時はderiving(詳細は後で)

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

レコード構文

• ex)人物を表す型を定義

data Person = Person { name :: String , age :: Int } deriving(Show)

レコード構文

• 今までのデータ型と違い,フィールドを取得する関数も生成される

ghci> :t name name :: Person -> String

レコード構文

• 値を作る際は順番は関係なし

ghci> let maeken = Person{age = 20,name="maeken"} ghci> maeken Person {name = "maeken", age = 20} ghci> name maeken "maeken"

型引数

• 複数の型を受け取ることが可能

• 受け取るものを型コンストラクタと呼ぶ

data Maybe a = Nothing | Just a↑ 型引数

型コンストラクタ ↓

型引数

• Maybeは型引数aがあるので多相的であると言う

• 他にもリスト[a]やEither a b等

型引数

• ex)三次元ベクトルの型を定義

data Vector a = Vector a a a deriving(Show)

型引数

• 型引数 aは数字を受け取りたい(Int,Float..)

vplus :: (Num a) => Vector a -> Vector a -> Vector a (Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n)

言葉おさらい

• data Maybe a = Nothing | Just a^^^^^^^^^ 型

^^^^^^^^^^ 値

^^^^^^^^ 値

型コンストラクタ ↓

値コンストラクタ ↓

言葉おさらい

• data Maybe a = Nothing | Just a^^^^^^^^^ 型

^^^^^^^^^^ 値

^^^^^^^^ 値

型コンストラクタ ↓

値コンストラクタ ↓

インスタンス自動導出

• derivingでその型を自動的に指定した型クラスのインスタンスにする

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq,Ord,Show,Read,Bounded,Enum)

Showクラス

• Showインスタンスは文字列に変換が可能

ghci> Wednesday Wednesday ghci> show Wednesday “Wednesday"

Readクラス

• Readインスタンスは文字列から変換が可能

ghci> read "Wednesday" :: Day Wednesday

Eqクラス

• Eqインスタンスは等しいかどうかが可能

ghci> Saturday == Sunday False ghci> Saturday /= Sunday True

Ordクラス

• Ordインスタンスは比較が可能

• 先に定義されてるほうが小さいとされる

ghci> Saturday > Friday True ghci> Monday `compare` Wednesday LT

Boundedクラス

• Boundedインスタンスは上限下限の取得が可能

ghci> minBound :: Day Monday ghci> maxBound :: Day Sunday

Enumクラス

• Enumインスタンスは要素の列を列挙可能

ghci> succ Monday Tuesday ghci> [Thursday .. Sunday] [Thursday,Friday,Saturday,Sunday]

型シノニム

型シノニム

• 型に別名を与える

• String = [Char]は実は型シノニム

型シノニム

• typeキーワード

type PhoneBook = [(String,String)]

型シノニム

• 電話帳の型の型シノニムを定義

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

再帰データ構造

再度おさらい

• data Maybe a = Nothing | Just a^^^^^^^^ 型

^^^^^^^^^^ 値

^^^^^^^^ 値

型コンストラクタ ↓

値コンストラクタ ↓

再度おさらい

• data Maybe a = Nothing | Just a

aには型が入る ↓

aは型引数 ↓

再度おさらい

• data Maybe a = Nothing | Just a

aには型が入る ↓

↑ 自分自身の型が入ると..?

再帰データ構造

• 独自のリスト型を定義

data List a = Empty | Cons a (List a) deriving(Show,Read,Eq,Ord)

再帰データ構造

• 実際のリストとの比較

ghci> Empty Empty ghci> 4 `Cons` (5 `Cons` Empty) Cons 4 (Cons 5 Empty)

ghci> [] [] ghci> 4 : (5 : []) [4,5]

演算子

• Consを中置換数に

• infixrで結合性を宣言.infixr 7 *,infixr 6 +等

infixr 5 :-: data List a = Empty | :-: a (List a) deriving(Show,Read,Eq,Ord)

演算子

• リストを結合する演算子を定義

infixr 5 ^++ (^++) :: List a -> List a -> List a Empty ^++ ys = ys (x :-: xs) ^++ ys = x :-: (xs ^++ ys)

ツリー構造

• ツリー構造を定義

data Nodes a = Node (Nodes a) (Nodes a) | Leaf a deriving (Show)

ツリー構造

• 深さ優先でリスト化

flatten :: Nodes a -> [a] flatten (Node s t) = (flattenT s)++(flattenT t) flatten (Leaf x) = [x]

型クラス

型クラス

• 型クラスは特定の振る舞いを定義する

• ある型Tがある型クラスCのインスタンスであるとは,型クラスCが定義する関数を型Tに対して使えるという事

• ※オブジェクト指向で言うクラスとは違うので注意

型クラス内部

• 例)Eq型クラス class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)

型クラス内部

• aは型変数.将来Eqのインスタンスとなる class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)

インスタンスを作る

• Eqのインスタンスを手動で生成 data TrafficLight = Red | Yellow | Green

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

インスタンスを作る• 型変数aに具体的な型が入っている

class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)

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

インスタンスを作る

• Showのインスタンスを手動で生成

instance Show TrafficLight where show Red = "Red light" show Yellow = "Yellow light" show Green = "Green light"

インスタンスを作る

ghci> Red Red light ghci> Red == Red True ghci> [Red,Green,Yellow] [Red light,Green light,Yellow light]

サブクラス化

• 例)Num型クラス

• 「ある型をNumのインスタンスにしたかったらその前にEqのインスタンスにする必要がある」

class (Eq a) => Num a where …

多相型の注意

• MaybeをEqのインスタンスにする場合

• 下記はまちがい.Maybeは型ではなく型コンストラクタ

instance Eq Maybe where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False

多相型の注意

• まだまちがい

• Maybeの中身に対して==を使っている

• つまりmはEqに属してないといけない instance Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False

多相型の注意

• せいかい

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

型クラスを作る

• JavaScriptの真理値を実装する

if(0) alert("Yes!") else alert("No!") if("") alert("Yes!") else alert("No!") if(false) alert("Yes!") else alert("No!")

⇛すべて"No!"が返る

型クラスを作る

• YesNo型クラスを定義

class YesNo a where yesno :: a -> Bool

YesNoインスタンス

• Int

• リスト

• Bool

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

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

instance YesNo Bool where yesno = id

YesNoインスタンス

• Maybe

• TrafficLight

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

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

YesNoインスタンス• 早速遊んでみる

ghci> yesno [] False ghci> yesno "aaa" True ghci> yesno (Just 0) True ghci> yesno Red False ghci> :t yesno yesno :: YesNo a => a -> Bool

YesNoインスタンス

• ついでに関数も作る

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

YesNoインスタンス

ghci> yesnoIf [] "yes!" "no!" "no!" ghci> yesnoIf "0" "yes!" "no!" "yes!" ghci> yesnoIf True "yes!" "no!" "yes!" ghci> yesnoIf Nothing "yes!" "no!" "no!"

Functor

Functor型クラス

• Functorは「全体を写す」ものの型クラス

• リストのmapも写す操作をする

• →リストはFunctor

Functor型クラス

• Functor型クラス実装を見る

class Functor f where fmap :: (a -> b) -> f a -> f b

Functor型クラス

• fは具体型ではなく型コンストラクタ

• fmapは,「ある型aから別の型bへの関数」と「ある型aに適用されたファンクター値」を取り,「別の型bへ適用されたファンクター値」を返す関数と読める

( ゚д゚)

mapとfmap

• 実はmapはfmapのリスト版

• mapは「型aから型bへの関数」と「型aのリスト」を受け取り,「型bのリスト」を返す

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

MaybeもFunctor?

• Functorは箱のような働きをすると考えられる

• fmapは箱の中身に対して関数を適用する

• Maybeも箱のようなもの…?

MaybeもFunctor

• Maybe型はFanctorなのでfmap可能

instance Functor Maybe where fmap f (Just x) = Just (f x) fmap Nothing = Nothing

NodesもFunctor

• Nodes型にもfmap可能.インスタンスは下記.

instance Functor Nodes where fmap f (Leaf a) = (Leaf (f a)) fmap f (Node left right) = Node (fmap f left) (fmap f right)

Functorで遊ぶ

ghci> fmap (+5) (Just 4) Just 9 ghci> fmap (++"!") (Just "maeken") Just “maeken!" ghci> let x=Node (Node (Leaf 1) (Node (Leaf 2) (Leaf 4))) (Leaf 3) ghci> fmap (*2) x Node (Node (Leaf 2) (Node (Leaf 4) (Leaf 8))) (Leaf 6)

練習問題

考え中

次回予告

Hello World