SlideShare ist ein Scribd-Unternehmen logo
1 von 48
Downloaden Sie, um offline zu lesen
型や型クラスを自分で
作ろう (前編)
すごいHaskell読書会 in 大阪 2週目 #7
2014-07-16
Suguru Hamazaki
Making Our Own Types and Type Classes
7章前半の内容
データ型の
定義方法
レコード構文
型引数 インスタンスの
自動導出
型シノニム
新しいデータ型を
定義する
Defining a New Data Type
• 標準ライブラリーでどのように定義されている
か?
• Bool
• Int
data Bool = False | True
型名 値コンスト
ラクター
値コンスト
ラクター
data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
実際の定義とは異なるが、
このように考えられる
形づくる
Shaping Up
• Shape型
• 2次元上の円と長方形を表現する
• area関数
• Shape型のデータの面積を求める
data Shape = Circle Float Float Float | Rectangle Float Float Float Float	
deriving (Show)
型名
値コンスト
ラクター
値コンスト
ラクター
値コンストラクター
• 値を作るので、値コンストラクター
• 実際には関数の一種
• ex) Circle は Float 型の値を3つ引数として受け取り、Shape
型の値を返す関数
• ex) False は?
• 型と値コンストラクターを混同しないよう注意
• 型名と値コンストラクターの名前が同じでもよい
Value Constructors
area :: Shape -> Float	
area (Circle _ _ r) = pi * r ^ 2	
area (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
型シグネチャーには、
型名であるShape を使う
(Circle や Rectangle は型名ではない)
パターンマッチには
コンストラクターが使える
• Point型
• Circle, Rectangle のフィールドを構成する中間的な
型
• nudge関数
• Shape型のデータを移動
• baseCircle, baseRect関数
• ファクトリー的なもの
data Point = Point Float Float deriving (Show)	
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
Circle, Rectangle のフィールドを
Point 型で定義して整理
area :: Shape -> Float	
area (Circle _ r) = pi * r ^ 2	
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
パターンマッチも
若干すっきり (?)
nudge :: Shape -> Float -> Float -> Shape	
nudge (Circle (Point x y) r) a b = Circle (Point (x + a) (y + b)) r	
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1 + a) (y1 + b))
(Point (x2 + a) (y2 + b))	
!
baseCircle :: Float -> Shape	
baseCircle r = Circle (Point 0 0) r	
!
baseRect :: Float -> Float -> Shape	
baseRect width height = Rectangle (Point 0 0) (Point width height)	
baseCircle
baseRectangle
モジュールからエクスポート
module Shapes	
( Point(..)	
, Shape(..)	
, area	
, nudge	
, baseCircle	
, baseRect	
) where
値コンストラクターを
全てエクスポートする記法
明示的に列挙してもよい
• 型のみエクスポートして、値コンストラクターを
エクスポートしないのも OK
• 実装を隠 できる
レコード構文
Record Syntax
data Person = Person String String Int Float String String deriving (Show)	
!
firstName :: Person -> String	
firstName (Person firstname _ _ _ _ _) = firstname	
!
lastName :: Person -> String	
lastName (Person _ lastname _ _ _ _) = lastname	
!
age :: Person -> Int	
age (Person _ _ age _ _ _) = age	
!
height :: Person -> Float	
height (Person _ _ _ height _ _) = height	
!
phoneNumber :: Person -> String	
phoneNumber (Person _ _ _ _ number _) = number	
!
flavor :: Person -> String	
flavor (Person _ _ _ _ _ flavor) = flavor
data Person = Person { firstName :: String	
, lastName :: String	
, age :: Int	
, height :: Float	
, phoneNumber :: String	
, flavor :: String } deriving (Show)	
それぞれ、フィールド名とその
型名を指定する
ghci> :t firstName	
firstName :: Person -> String	 対応する関数が自動的
に作られる
data Car = Car { company :: String	
, model :: String	
, year :: Int } deriving (Show)	
ghci> Car { company = "Ford", model = "Mustang", year = 1967}	
Car {company = "Ford", model = "Mustang", year = 1967}	
ghci> Car { company = "Ford", year = 1967, model = "Mustang"}	
Car {company = "Ford", model = "Mustang", year = 1967}	
フィールドを任意の順
番で指定できる
(型クラス Show を
derive した場合) 出力
が整形される
• レコード構文が役に立つケース
• フィールドが複数あって、
• どれがどれに対応するのかわかりにくい場合
型引数
Type Parameters
data Maybe a = Nothing | Just a
型コンストラクター
型引数
• 値コンストラクターは、
• 値を引数に取り、
• 値を作る
• 型コンストラクターは、
• 型を引数に取り (型引数)、
• 型を作る
• Maybe は型ではなく、型コンストラクター
• Maybe Char は型
• Just ‘a’ は Maybe Char 型の値
• Nothing は Maybe a 型の値
• Maybe a は polymorphic な型
• Maybe Int, Maybe Char, etc. として振る舞える
型引数を取らなくても
(0個取っても)
型コンストラクターって
言うのかな?
Carをパラメーター化すると?
data Car a b c = Car { company :: a	
, model :: b	
, year :: c } deriving (Show)
tellCar :: (Show a) => Car String String a -> String	
tellCar (Car { company = c, model = m, year = y}) =	
"This " ++ c ++ " " ++ m ++ " was made in " ++ show y	
tellCar では year の型
しかパラメーター化さ
れてない
結局、ほとんどのケースで
Car String String Int 型を
使うことになりそう
型引数を使うと良いケース
• 値コンストラクターに含まれる型が、どんなも
のでも構わないケース
• ex) Maybe a, [a], Data.Map k a
データ宣言に型クラス制約は
加えない
data (Ord k) => Map k v = ...
このような制約は
(文法上は正しいが)
規約上、付けない
• 必要な関数の型宣言に付ければ済む
• 必要の無い関数の型宣言に付けないで済む
3次元ベクトルを表わす
Vector a 型 の場合
data Vector a = Vector a a a deriving (Show)	
!
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)	
!
dotProd :: (Num a) => Vector a -> Vector a -> a	
(Vector i j k) `dotProd` (Vector l m n) = i * l + j * m + k * n	
!
vmult :: (Num a) => Vector a -> a -> Vector a	
(Vector i j k) `vmult` m = Vector (i * m) (j * m) (k * m)	
型クラス制約は
付けない
関数の方に型クラ
ス制約を付ける
インスタンスの
自動導出
Derived Instances
型クラス
• オブジェクト指向プログラミングにおける「クラス」と混
同しないよう注意
• OOPのクラスは、そのクラスから作られたオブジェクト
が持つ性質を規定
• Haskell の型クラスは、型が型クラスのインスタンスにな
り、その型の性質を定義する
• deriving キーワードを使って、ある型を型クラスのインスタ
ンスにすることができる
data Person = Person { firstName :: String	
, lastName :: String	
, age :: Int	
} deriving (Eq)
1. Person型の値同士を == または /= で比べた時、マッチする値コンスト
ラクターを探す
2. 見付かった値コンストラクターのフィールド同士を、それぞれ == また
は /= で比べる
• 全てのフィールドの型が Eq のインスタンスでなければならない
mikeD = Person { firstName = "Michael"	
, lastName = "Diamond"	
, age = 43 }	
!
adRock = Person { firstName = "Adam"	
, lastName = "Horovitz"	
, age = 41 }	
!
mca = Person { firstName = "Adam"	
, lastName = "Yauch"	
, age = 44 }	
ghci> mikeD == adRock	
False	
ghci> mikeD == mikeD	
True	
ghci> mikeD == Person { firstName = "Michael", lastName = "Diamond", age = 43 }	
True
• Eq
• equality / inequality についてテストできる
• Show
• 値をStringへ変換できる
• Read
• Stringをパーズして値を作れる
• Ord
• 大小比較、順序付けできる
• Bounded
• 上限 (maxBound) 、下限 (minBound) がある
• Enum
• 順番に列挙することができる
data Person = Person { firstName :: String	
, lastName :: String	
, age :: Int	
} deriving (Eq, Show, Read)	
!
mikeD = Person { firstName = "Michael"	
, lastName = "Diamond"	
, age = 43 }	
!
mysteryDude = "Person { firstName = "Michael"" ++	
", lastName = "Diamond"" ++	
", age = 43}"	
ghci> read mysteryDude :: Person	
Person {firstName = "Michael", lastName = "Diamond", age = 43}	
ghci> read mysteryDude == mikeD	
True	
String 型を read
して Person 型に
型クラス Show の show が使えるので、
値をGHCi コンソールに出力できる
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday	
deriving (Eq, Ord, Show, Read, Bounded, Enum)	
!
λ> Saturday > Sunday	
False	
λ> Monday `compare` Wednesday	
LT	
λ> minBound :: Day	
Monday	
λ> maxBound :: Day	
Sunday	
λ> succ Monday	
Tuesday	
λ> pred $ succ Monday	
Monday	
λ> [Thursday .. Sunday]	
[Thursday,Friday,Saturday,Sunday]	
λ> [minBound .. maxBound] :: [Day]	
[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]	
Ord による大小比較
Bound による上限
と下限
Enum による順次的な
列挙
上限、下限を利用した、
順次的な列挙
型シノニム
Type Synonyms
type String = [Char]
• 既存の型の別名を定義
• 新しい型が作られるのではない
type PhoneNumber = String	
type Name = String	
type PhoneBook = [(Name,PhoneNumber)]
inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool	
inPhoneBook name pnumber pbook = (name, pnumber) `elem` pbook
より明確な意図を伝える
型シグネチャー
!
!
!
book = [("betty", "555-2938")	
,("bonnie", "452-2928")	
,("patsy", "493-2928")	
,("lucille", "205-2928")	
,("wendy", "939-8282")	
,("penny", "853-2492")	
]	
λ> inPhoneBook "wendy" "939-8282" book	
True
型シノニムの型パラメーター化
type AssocList k v = [(k, v)]
type IntMap v = Map Int v
型パラメーターを持つ
型シノニムの定義
型パラメーターを
部分適用した型シノニム
AssocList, IntMap は
型コンストラクターになる
!
値コンストラクターと
混同しないよう注意
data Either a b = Left a | Right b	
deriving (Eq, Ord, Read, Show)
• ある型か他のある型の値を表現
• Maybe a と同様に、処理の結果を表わすのに
よく使われる
• 失敗した場合もデータを保持できるのが違
い
ロッカーの例
import qualified Data.Map as Map	
!
data LockerState = Taken | Free deriving (Show, Eq)	
!
type Code = String	
!
type LockerMap = Map.Map Int (LockerState, Code)	
!
lockerLookup :: Int -> LockerMap -> Either String Code	
lockerLookup lockerNumber map = case Map.lookup lockerNumber map of	
Nothing -> Left $ "Locker " ++ show lockerNumber ++ " doesn't exist!"	
Just (state, code) -> if state /= Taken	
then Right code	
else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"	
!
lockers :: LockerMap	
lockers = Map.fromList	
[(100,(Taken, "ZD39I"))	
,(101,(Free,"JAH3I"))	
,(103,(Free,"IQSA9"))	
,(105,(Free,"QOTSA"))	
,(109,(Taken,"893JJ"))	
,(110,(Taken,"99292"))]
練習問題
• 上のような構造を持つ、URIを表現する型を作ってみましょう
• query は複数の key, value のペアを持ちます
• 必須の要素とオプショナルの要素があることに注意して下さい
https://user:password@example.com:80/path/somewhere?foo=bar#baz
scheme userinfo host port path query fragment
authority

Weitere ähnliche Inhalte

Was ist angesagt?

Object-Funcational Analysis and design
Object-Funcational Analysis and designObject-Funcational Analysis and design
Object-Funcational Analysis and designTomoharu ASAMI
 
What Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットWhat Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットTaisuke Oe
 
実務者のためのかんたんScalaz
実務者のためのかんたんScalaz実務者のためのかんたんScalaz
実務者のためのかんたんScalazTomoharu ASAMI
 
Strings and Characters in Swift
Strings and Characters in SwiftStrings and Characters in Swift
Strings and Characters in SwiftGoichi Hirakawa
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるHideyuki Tanaka
 
すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章aomori ringo
 
Haskell勉強会2 in ie
Haskell勉強会2 in ieHaskell勉強会2 in ie
Haskell勉強会2 in iemaeken2010
 
Scalaの文字列処理 Day 3 コードポイントとサロゲートペア
Scalaの文字列処理 Day 3 コードポイントとサロゲートペアScalaの文字列処理 Day 3 コードポイントとサロゲートペア
Scalaの文字列処理 Day 3 コードポイントとサロゲートペアynupc
 
オブジェクト指向開発におけるObject-Functional Programming
オブジェクト指向開発におけるObject-Functional Programmingオブジェクト指向開発におけるObject-Functional Programming
オブジェクト指向開発におけるObject-Functional ProgrammingTomoharu ASAMI
 
明日から業務で使うScala
明日から業務で使うScala明日から業務で使うScala
明日から業務で使うScalamartin_lover_ca
 
Sns suite presentation
Sns suite presentationSns suite presentation
Sns suite presentationJason Namkung
 
AuxパターンをDottyで解決する
AuxパターンをDottyで解決するAuxパターンをDottyで解決する
AuxパターンをDottyで解決するTaisuke Oe
 
Object-Functional Analysis and Design and Programming温泉
Object-Functional Analysis and Design and Programming温泉Object-Functional Analysis and Design and Programming温泉
Object-Functional Analysis and Design and Programming温泉Tomoharu ASAMI
 

Was ist angesagt? (20)

Object-Funcational Analysis and design
Object-Funcational Analysis and designObject-Funcational Analysis and design
Object-Funcational Analysis and design
 
What Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットWhat Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミット
 
 
  
 
 
実務者のためのかんたんScalaz
実務者のためのかんたんScalaz実務者のためのかんたんScalaz
実務者のためのかんたんScalaz
 
MP in Scala
MP in ScalaMP in Scala
MP in Scala
 
Strings and Characters in Swift
Strings and Characters in SwiftStrings and Characters in Swift
Strings and Characters in Swift
 
Lecture2
Lecture2Lecture2
Lecture2
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章
 
MP in Haskell
MP in HaskellMP in Haskell
MP in Haskell
 
Haskell勉強会2 in ie
Haskell勉強会2 in ieHaskell勉強会2 in ie
Haskell勉強会2 in ie
 
Scalaの文字列処理 Day 3 コードポイントとサロゲートペア
Scalaの文字列処理 Day 3 コードポイントとサロゲートペアScalaの文字列処理 Day 3 コードポイントとサロゲートペア
Scalaの文字列処理 Day 3 コードポイントとサロゲートペア
 
Haskell Lecture 2
Haskell Lecture 2Haskell Lecture 2
Haskell Lecture 2
 
Pythonintro
PythonintroPythonintro
Pythonintro
 
オブジェクト指向開発におけるObject-Functional Programming
オブジェクト指向開発におけるObject-Functional Programmingオブジェクト指向開発におけるObject-Functional Programming
オブジェクト指向開発におけるObject-Functional Programming
 
明日から業務で使うScala
明日から業務で使うScala明日から業務で使うScala
明日から業務で使うScala
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
Sns suite presentation
Sns suite presentationSns suite presentation
Sns suite presentation
 
AuxパターンをDottyで解決する
AuxパターンをDottyで解決するAuxパターンをDottyで解決する
AuxパターンをDottyで解決する
 
Object-Functional Analysis and Design and Programming温泉
Object-Functional Analysis and Design and Programming温泉Object-Functional Analysis and Design and Programming温泉
Object-Functional Analysis and Design and Programming温泉
 

Ähnlich wie すごいHaskell読書会 第7章 (前編)

How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜Hiromi Ishii
 
C++0x 言語の未来を語る
C++0x 言語の未来を語るC++0x 言語の未来を語る
C++0x 言語の未来を語るAkira Takahashi
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexprGenya Murakami
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Prunus 1350
 
Scalaプログラミング・マニアックス
Scalaプログラミング・マニアックスScalaプログラミング・マニアックス
Scalaプログラミング・マニアックスTomoharu ASAMI
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)啓 小笠原
 
つくってあそぼ ラムダ計算インタプリタ
つくってあそぼ ラムダ計算インタプリタつくってあそぼ ラムダ計算インタプリタ
つくってあそぼ ラムダ計算インタプリタ京大 マイコンクラブ
 
第1回R勉強会@東京
第1回R勉強会@東京第1回R勉強会@東京
第1回R勉強会@東京Yohei Sato
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成Akineko Shimizu
 
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと 12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと Haruka Ozaki
 
データとは何か
データとは何かデータとは何か
データとは何かKenta Suzuki
 
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesasm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesNoritada Shimizu
 
すごい Haskell 読書会 in 大阪 #5
すごい Haskell 読書会 in 大阪 #5すごい Haskell 読書会 in 大阪 #5
すごい Haskell 読書会 in 大阪 #5Kazkuki Oakamoto
 
Weka分類学習アルゴリズムの利用法(その1)
Weka分類学習アルゴリズムの利用法(その1)Weka分類学習アルゴリズムの利用法(その1)
Weka分類学習アルゴリズムの利用法(その1)Hidenao Abe
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexprGenya Murakami
 
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字ynupc
 

Ähnlich wie すごいHaskell読書会 第7章 (前編) (20)

How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
 
Thinking in Cats
Thinking in CatsThinking in Cats
Thinking in Cats
 
C++0x 言語の未来を語る
C++0x 言語の未来を語るC++0x 言語の未来を語る
C++0x 言語の未来を語る
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexpr
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章
 
Scalaプログラミング・マニアックス
Scalaプログラミング・マニアックスScalaプログラミング・マニアックス
Scalaプログラミング・マニアックス
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
 
つくってあそぼ ラムダ計算インタプリタ
つくってあそぼ ラムダ計算インタプリタつくってあそぼ ラムダ計算インタプリタ
つくってあそぼ ラムダ計算インタプリタ
 
第1回R勉強会@東京
第1回R勉強会@東京第1回R勉強会@東京
第1回R勉強会@東京
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成
 
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと 12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
12-11-30 Kashiwa.R #5 初めてのR Rを始める前に知っておきたい10のこと
 
データとは何か
データとは何かデータとは何か
データとは何か
 
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesasm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
 
すごい Haskell 読書会 in 大阪 #5
すごい Haskell 読書会 in 大阪 #5すごい Haskell 読書会 in 大阪 #5
すごい Haskell 読書会 in 大阪 #5
 
Weka分類学習アルゴリズムの利用法(その1)
Weka分類学習アルゴリズムの利用法(その1)Weka分類学習アルゴリズムの利用法(その1)
Weka分類学習アルゴリズムの利用法(その1)
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr
 
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字
Scalaの文字列処理 Day 2 リテラル・補間子・特殊な文字
 
Python opt
Python optPython opt
Python opt
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
Ssaw08 0930
Ssaw08 0930Ssaw08 0930
Ssaw08 0930
 

すごいHaskell読書会 第7章 (前編)

  • 1. 型や型クラスを自分で 作ろう (前編) すごいHaskell読書会 in 大阪 2週目 #7 2014-07-16 Suguru Hamazaki Making Our Own Types and Type Classes
  • 5. data Bool = False | True 型名 値コンスト ラクター 値コンスト ラクター
  • 6. data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647 実際の定義とは異なるが、 このように考えられる
  • 8. • Shape型 • 2次元上の円と長方形を表現する • area関数 • Shape型のデータの面積を求める
  • 9. data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) 型名 値コンスト ラクター 値コンスト ラクター
  • 10. 値コンストラクター • 値を作るので、値コンストラクター • 実際には関数の一種 • ex) Circle は Float 型の値を3つ引数として受け取り、Shape 型の値を返す関数 • ex) False は? • 型と値コンストラクターを混同しないよう注意 • 型名と値コンストラクターの名前が同じでもよい Value Constructors
  • 11. area :: Shape -> Float area (Circle _ _ r) = pi * r ^ 2 area (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1) 型シグネチャーには、 型名であるShape を使う (Circle や Rectangle は型名ではない) パターンマッチには コンストラクターが使える
  • 12. • Point型 • Circle, Rectangle のフィールドを構成する中間的な 型 • nudge関数 • Shape型のデータを移動 • baseCircle, baseRect関数 • ファクトリー的なもの
  • 13. data Point = Point Float Float deriving (Show) data Shape = Circle Point Float | Rectangle Point Point deriving (Show) Circle, Rectangle のフィールドを Point 型で定義して整理
  • 14. area :: Shape -> Float area (Circle _ r) = pi * r ^ 2 area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1) パターンマッチも 若干すっきり (?)
  • 15. nudge :: Shape -> Float -> Float -> Shape nudge (Circle (Point x y) r) a b = Circle (Point (x + a) (y + b)) r nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1 + a) (y1 + b)) (Point (x2 + a) (y2 + b)) ! baseCircle :: Float -> Shape baseCircle r = Circle (Point 0 0) r ! baseRect :: Float -> Float -> Shape baseRect width height = Rectangle (Point 0 0) (Point width height) baseCircle baseRectangle
  • 16. モジュールからエクスポート module Shapes ( Point(..) , Shape(..) , area , nudge , baseCircle , baseRect ) where 値コンストラクターを 全てエクスポートする記法 明示的に列挙してもよい
  • 19. data Person = Person String String Int Float String String deriving (Show) ! firstName :: Person -> String firstName (Person firstname _ _ _ _ _) = firstname ! lastName :: Person -> String lastName (Person _ lastname _ _ _ _) = lastname ! age :: Person -> Int age (Person _ _ age _ _ _) = age ! height :: Person -> Float height (Person _ _ _ height _ _) = height ! phoneNumber :: Person -> String phoneNumber (Person _ _ _ _ number _) = number ! flavor :: Person -> String flavor (Person _ _ _ _ _ flavor) = flavor
  • 20. data Person = Person { firstName :: String , lastName :: String , age :: Int , height :: Float , phoneNumber :: String , flavor :: String } deriving (Show) それぞれ、フィールド名とその 型名を指定する ghci> :t firstName firstName :: Person -> String 対応する関数が自動的 に作られる
  • 21. data Car = Car { company :: String , model :: String , year :: Int } deriving (Show) ghci> Car { company = "Ford", model = "Mustang", year = 1967} Car {company = "Ford", model = "Mustang", year = 1967} ghci> Car { company = "Ford", year = 1967, model = "Mustang"} Car {company = "Ford", model = "Mustang", year = 1967} フィールドを任意の順 番で指定できる (型クラス Show を derive した場合) 出力 が整形される
  • 22. • レコード構文が役に立つケース • フィールドが複数あって、 • どれがどれに対応するのかわかりにくい場合
  • 24. data Maybe a = Nothing | Just a 型コンストラクター 型引数
  • 25. • 値コンストラクターは、 • 値を引数に取り、 • 値を作る • 型コンストラクターは、 • 型を引数に取り (型引数)、 • 型を作る
  • 26. • Maybe は型ではなく、型コンストラクター • Maybe Char は型 • Just ‘a’ は Maybe Char 型の値 • Nothing は Maybe a 型の値 • Maybe a は polymorphic な型 • Maybe Int, Maybe Char, etc. として振る舞える
  • 28. Carをパラメーター化すると? data Car a b c = Car { company :: a , model :: b , year :: c } deriving (Show) tellCar :: (Show a) => Car String String a -> String tellCar (Car { company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y tellCar では year の型 しかパラメーター化さ れてない 結局、ほとんどのケースで Car String String Int 型を 使うことになりそう
  • 30. データ宣言に型クラス制約は 加えない data (Ord k) => Map k v = ... このような制約は (文法上は正しいが) 規約上、付けない • 必要な関数の型宣言に付ければ済む • 必要の無い関数の型宣言に付けないで済む
  • 31. 3次元ベクトルを表わす Vector a 型 の場合 data Vector a = Vector a a a deriving (Show) ! 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) ! dotProd :: (Num a) => Vector a -> Vector a -> a (Vector i j k) `dotProd` (Vector l m n) = i * l + j * m + k * n ! vmult :: (Num a) => Vector a -> a -> Vector a (Vector i j k) `vmult` m = Vector (i * m) (j * m) (k * m) 型クラス制約は 付けない 関数の方に型クラ ス制約を付ける
  • 33. 型クラス • オブジェクト指向プログラミングにおける「クラス」と混 同しないよう注意 • OOPのクラスは、そのクラスから作られたオブジェクト が持つ性質を規定 • Haskell の型クラスは、型が型クラスのインスタンスにな り、その型の性質を定義する • deriving キーワードを使って、ある型を型クラスのインスタ ンスにすることができる
  • 34. data Person = Person { firstName :: String , lastName :: String , age :: Int } deriving (Eq) 1. Person型の値同士を == または /= で比べた時、マッチする値コンスト ラクターを探す 2. 見付かった値コンストラクターのフィールド同士を、それぞれ == また は /= で比べる • 全てのフィールドの型が Eq のインスタンスでなければならない
  • 35. mikeD = Person { firstName = "Michael" , lastName = "Diamond" , age = 43 } ! adRock = Person { firstName = "Adam" , lastName = "Horovitz" , age = 41 } ! mca = Person { firstName = "Adam" , lastName = "Yauch" , age = 44 } ghci> mikeD == adRock False ghci> mikeD == mikeD True ghci> mikeD == Person { firstName = "Michael", lastName = "Diamond", age = 43 } True
  • 36. • Eq • equality / inequality についてテストできる • Show • 値をStringへ変換できる • Read • Stringをパーズして値を作れる • Ord • 大小比較、順序付けできる • Bounded • 上限 (maxBound) 、下限 (minBound) がある • Enum • 順番に列挙することができる
  • 37. data Person = Person { firstName :: String , lastName :: String , age :: Int } deriving (Eq, Show, Read) ! mikeD = Person { firstName = "Michael" , lastName = "Diamond" , age = 43 } ! mysteryDude = "Person { firstName = "Michael"" ++ ", lastName = "Diamond"" ++ ", age = 43}" ghci> read mysteryDude :: Person Person {firstName = "Michael", lastName = "Diamond", age = 43} ghci> read mysteryDude == mikeD True String 型を read して Person 型に 型クラス Show の show が使えるので、 値をGHCi コンソールに出力できる
  • 38. data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Ord, Show, Read, Bounded, Enum) !
  • 39. λ> Saturday > Sunday False λ> Monday `compare` Wednesday LT λ> minBound :: Day Monday λ> maxBound :: Day Sunday λ> succ Monday Tuesday λ> pred $ succ Monday Monday λ> [Thursday .. Sunday] [Thursday,Friday,Saturday,Sunday] λ> [minBound .. maxBound] :: [Day] [Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday] Ord による大小比較 Bound による上限 と下限 Enum による順次的な 列挙 上限、下限を利用した、 順次的な列挙
  • 41. type String = [Char] • 既存の型の別名を定義 • 新しい型が作られるのではない
  • 42. type PhoneNumber = String type Name = String type PhoneBook = [(Name,PhoneNumber)] inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool inPhoneBook name pnumber pbook = (name, pnumber) `elem` pbook より明確な意図を伝える 型シグネチャー
  • 43. ! ! ! book = [("betty", "555-2938") ,("bonnie", "452-2928") ,("patsy", "493-2928") ,("lucille", "205-2928") ,("wendy", "939-8282") ,("penny", "853-2492") ] λ> inPhoneBook "wendy" "939-8282" book True
  • 44. 型シノニムの型パラメーター化 type AssocList k v = [(k, v)] type IntMap v = Map Int v 型パラメーターを持つ 型シノニムの定義 型パラメーターを 部分適用した型シノニム AssocList, IntMap は 型コンストラクターになる ! 値コンストラクターと 混同しないよう注意
  • 45. data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show) • ある型か他のある型の値を表現 • Maybe a と同様に、処理の結果を表わすのに よく使われる • 失敗した場合もデータを保持できるのが違 い
  • 47. import qualified Data.Map as Map ! data LockerState = Taken | Free deriving (Show, Eq) ! type Code = String ! type LockerMap = Map.Map Int (LockerState, Code) ! lockerLookup :: Int -> LockerMap -> Either String Code lockerLookup lockerNumber map = case Map.lookup lockerNumber map of Nothing -> Left $ "Locker " ++ show lockerNumber ++ " doesn't exist!" Just (state, code) -> if state /= Taken then Right code else Left $ "Locker " ++ show lockerNumber ++ " is already taken!" ! lockers :: LockerMap lockers = Map.fromList [(100,(Taken, "ZD39I")) ,(101,(Free,"JAH3I")) ,(103,(Free,"IQSA9")) ,(105,(Free,"QOTSA")) ,(109,(Taken,"893JJ")) ,(110,(Taken,"99292"))]
  • 48. 練習問題 • 上のような構造を持つ、URIを表現する型を作ってみましょう • query は複数の key, value のペアを持ちます • 必須の要素とオプショナルの要素があることに注意して下さい https://user:password@example.com:80/path/somewhere?foo=bar#baz scheme userinfo host port path query fragment authority