45
HaskellKinebuchi Tomohiko 2016-07-02 Kinebuchi Tomohiko Haskell

圏とHaskellの型

Embed Size (px)

Citation preview

Page 1: 圏とHaskellの型

圏とHaskellの型

Kinebuchi Tomohiko

2016-07-02

Kinebuchi Tomohiko 圏とHaskellの型

Page 2: 圏とHaskellの型

目次

想定聴者

下準備

スライドの表記について圏論とはプログラムの型とは

Haskellの型全体を圏として見る

Hask圏tuple型Either型Functor自然変換

参考文献

宣伝

数学茶屋

Kinebuchi Tomohiko 圏とHaskellの型

Page 3: 圏とHaskellの型

想定聴者

プログラムの型は分かるが圏論を知らない人

圏論は知っているがプログラムの型を知らない人

→ プログラムの型が圏論を通してどう見えるかを解説していきます.

Kinebuchi Tomohiko 圏とHaskellの型

Page 4: 圏とHaskellの型

話すこと

Haskellの型の仕組みと圏論の対応Haskellの型システム 圏論の概念

型 対象

関数 射

tuple 直積

either 直和

Functor 関手

(ある種の関数) 自然変換

Haskellの型システムが圏論に支えられている実例を見ていきます.

Kinebuchi Tomohiko 圏とHaskellの型

Page 5: 圏とHaskellの型

話さないこと

圏論一般の説明前の発表を参照してください

プログラムの圏論的意味論

Haskellの文法

Haskellの良いプログラミング

この発表を聞いてもHaskellが書けるようになるわけではないです. 悪しからず.

Kinebuchi Tomohiko 圏とHaskellの型

Page 6: 圏とHaskellの型

下準備 - スライドの表記について

スライドのタイトルに目印を付け, どちら側の話なのかを表します.

プログラミング側の話→ [プ]

圏論側の話→ [圏]

Kinebuchi Tomohiko 圏とHaskellの型

Page 7: 圏とHaskellの型

スライドとソースコードの入手先

https://github.com/TomohikoK/math-cafe-20160702LaTeXファイルとHaskellソースコードを手に入れて嬉しい人向けです. 何かあれば私のTwitterアカウント(https://twitter.com/kinebuchitomo) に話し掛けるか,GitHubのissueに書いてください..hsという拡張子のファイルは, ghciで:loadコマンドで読み込めます.

Kinebuchi Tomohiko 圏とHaskellの型

Page 8: 圏とHaskellの型

[圏] 圏論とは

(あくまでこの発表での圏論の使い方ですが)「対象」と対象どうしをつなぐ「射」やそれらの「性質」という言葉で物事を記述する手法.対象について語るとき, 他の対象との関係の性質という外部からの視点だけを使い, 「何からできているか?」という内部からの視点を使わないという特徴がある.

Kinebuchi Tomohiko 圏とHaskellの型

Page 9: 圏とHaskellの型

[プ] プログラムの型とは

「型」とはプログラムに登場する値や式の種類のことで, その役割は

プログラムの間違いの検出整数が期待されているところに文字列が現れるなどの間違いを事前に見付ける.

抽象化, インターフェースの定義値や関数の重要な性質だけに着目する.

ソースコードを読みやすくする.「ソースコード」とはプログラムを人間が読める形で記述したもの.

プログラムの最適化型の情報を使って, より高速なプログラムを生成する.

ref.『Types and Programming Languages』(Benjamin C. Pierce)

Kinebuchi Tomohiko 圏とHaskellの型

Page 10: 圏とHaskellの型

[プ][圏] Haskellの型がなす圏 (Hask圏)

対象: 型

射: 一変数関数

恒等射: id関数 (引数をそのまま返す関数)射の合成: f . g (Haskellの意味での関数の合成)

関数fの変数の型がA, 返り値の型がBのとき, fをAからBへの射とします.(https://en.wikibooks.org/wiki/Haskell/Category theory)

Kinebuchi Tomohiko 圏とHaskellの型

Page 11: 圏とHaskellの型

[プ][圏] Haskellの型が圏をなすことの説明

恒等射についての条件:

任意の関数f, gに対してf . id == fとid . g == gが成り立ちます.

射の合成についての条件:

任意の関数f, g, hに対して(h . g) . f == h . (g . f)が成り立ちます.

→ Haskellの型が圏をなすことが分かりました.

Kinebuchi Tomohiko 圏とHaskellの型

Page 12: 圏とHaskellの型

[プ][圏] 例. Hask圏の対象

Integer: 整数の型

String: 文字列の型

Maybe String: 文字列の値がある状態もしくは値が無い状態を表す型

[Integer]: 整数のリストの型

Kinebuchi Tomohiko 圏とHaskellの型

Page 13: 圏とHaskellの型

[プ][圏] 例. Hask圏の射

Haskellの世界のdoubleと数学の世界のdoubleの見た目はなんだか似ている.

Haskellの関数double

double :: Integer -> Integer -- 関数の型宣言double x = 2 * x -- 関数の定義

数学の関数doubledouble : Z→ Zdouble(x) = 2x

double関数を射と見たときの始域はIntegerで終域もIntegerです. double関数の型宣言の情報のみを使っていて, 関数の定義の情報は使っていないことに注意してください.

Kinebuchi Tomohiko 圏とHaskellの型

Page 14: 圏とHaskellの型

[プ][圏] ここまでの内容で質疑応答

「あのスライドをもう1回見せてほしい」

「あそこの解説を聞き逃した」

「あれってどういう意味?」

Kinebuchi Tomohiko 圏とHaskellの型

Page 15: 圏とHaskellの型

[プ] Haskellの直積型 (tuple型)

型a, bに対し, それらの直積と呼ばれるtuple型(a, b)が存在し,tupleの基本的な関数としてfst, sndがあります.

-- | Extract the first component of a pair.fst :: (a,b) -> afst (x,_) = x

-- | Extract the second component of a pair.snd :: (a,b) -> bsnd (_,y) = y

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Data.Tuple.html#line-33)→ これに対応する圏論の概念は何でしょう?

Kinebuchi Tomohiko 圏とHaskellの型

Page 16: 圏とHaskellの型

[圏] 積の復習

圏Cの対象X, Yの積〈X × Y, p, q〉とは, 圏Cの対象X × Y ,射p : X × Y → X, 射q : X × Y → Yの組で, 次の条件を満たすものです.

W

k

}}

∃1h

��

l

!!X X × Ypoo

q// Y

(条件) 任意の圏Cの対象Wと射k :W → X, l :W → Yに対して, 射h :W → X × Yが1つだけあって

p ◦ h = k

q ◦ h = l

となる.Kinebuchi Tomohiko 圏とHaskellの型

Page 17: 圏とHaskellの型

[プ][圏] 直積型がHask圏の積であること

直積型がHask圏の積になるためには次の条件を満たす必要があります.(条件) a, b, cを任意の型とし, k :: c -> a, l :: c -> bを任意の関数としたとき, 関数h :: c -> (a, b)があって

fst . h == k

snd . h == l

とできる.

c

k

}}

∃1h

��

l

!!a (a, b)

fstoo

snd// b

→ h x = (k x, l x)と実装すれば条件を満たします.Kinebuchi Tomohiko 圏とHaskellの型

Page 18: 圏とHaskellの型

[プ] hの例

作為的な例ですが.

k :: (Integer, String, String) -> Integerk (x, _, _) = x

l :: (Integer, String, String) -> Stringl (_, y, _) = y

h :: (Integer, String, String) -> (Integer, String)h x = (k x, l x)

Kinebuchi Tomohiko 圏とHaskellの型

Page 19: 圏とHaskellの型

[プ] Haskellの直和型 (Either型)

型a, bに対し, それらの直和と呼ばれるEither型Either a bが存在し, EitherのコンストラクタとしてLeft, Rightがあります.

data Either a b = Left a | Right bderiving (Eq, Ord, Read, Show)

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Data.Either.html#Either)→ これに対応する圏論の概念は何でしょう?

Kinebuchi Tomohiko 圏とHaskellの型

Page 20: 圏とHaskellの型

[圏] 余積の復習

圏Cの対象X, Yの余積〈X∐Y, i, j〉とは, 圏Cの対象X

∐Y ,

射i : X → X∐Y , 射j : Y → Y

∐Yの組で, 次の条件を満たす

ものです.

W

X

k

==

i// X

∐Y

∃1h

OO

Yj

oo

l

aa

(条件) 任意の圏Cの対象Wと射k : X →W , l : Y →Wに対して, 射h : X

∐Y →Wが1つだけあって

h ◦ i = k

h ◦ j = l

となる.Kinebuchi Tomohiko 圏とHaskellの型

Page 21: 圏とHaskellの型

[プ][圏] 直和型がHask圏の余積であること

直和型がHask圏の余積になるためには次の条件を満たす必要があります.(条件) a, b, cを任意の型とし, k :: a -> c, l :: b -> cを任意の関数としたとき, 関数h :: Either a b -> cがあって

h . Left == k

h . Right == l

とできる.

c

a

k

;;

Left// Either a b

∃1h

OO

bRightoo

l

cc

Kinebuchi Tomohiko 圏とHaskellの型

Page 22: 圏とHaskellの型

[プ] hの例

k :: Integer -> Integerk x = 2 * x

l :: Integer -> Integerl x = 3 * x

h :: Either Integer Integer -> Integerh (Left x) = 2 * xh (Right x) = 3 * x

Haskellのパターンマッチでhを定義しているので, 条件を満たすことが分かりやすくなっています.

Kinebuchi Tomohiko 圏とHaskellの型

Page 23: 圏とHaskellの型

[プ][圏] ここまでの内容で質疑応答

「あのスライドをもう1回見せてほしい」

「あそこの解説を聞き逃した」

「あれってどういう意味?」

Kinebuchi Tomohiko 圏とHaskellの型

Page 24: 圏とHaskellの型

[プ] HaskellのFunctor

定義は以下の通りです.

class Functor f wherefmap :: (a -> b) -> f a -> f b

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#Functor)ある具体的なFunctorをfとして, fの射関数fmapの型を宣言しています.(「射関数」= 関手Fが写す対象と射のうち射の方だけを考えたもの. 射fを射Ffに写す関数のこと.)

Kinebuchi Tomohiko 圏とHaskellの型

Page 25: 圏とHaskellの型

[圏] 関手の復習

圏Cから圏Dへの関手F : C → Dとは, 対応の組F : Ob(C)→ Ob(D), F : HomC(X,Y )→ HomD(FX,FY )であって,

F1X = 1FX

F (g ◦ f) = Fg ◦ Ff

を満たすもの.

Kinebuchi Tomohiko 圏とHaskellの型

Page 26: 圏とHaskellの型

[プ] HaskellのFunctor則 (Functor law)

Functor則とはFunctorが満たすべき条件. これに反する実装自体はできるものの, そうするメリットはあまり無いです.

fmap id == idfmap (f . g) == fmap f . fmap g

(https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Functor.html#t:Functor)このFunctor則の2つの条件が, 関手が満たすべき2つの条件に対応しています.→ HaskellのFunctorと圏論での関手に対応

Kinebuchi Tomohiko 圏とHaskellの型

Page 27: 圏とHaskellの型

[プ] Functorの例: Maybe

Maybeとは, ある型の値がある状態と値がない空の状態をいっぺんに表せる型です. 定義は以下の通りです.

data Maybe a = Nothing | Just aderiving (Eq, Ord)

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#Maybe)Nothingが「値が無いこと」に対応し,Just aが「aという型の値があること」に対応しています.

Kinebuchi Tomohiko 圏とHaskellの型

Page 28: 圏とHaskellの型

[プ] MaybeがFunctor則を満たすこと (1/2)

MaybeのFunctorとしての定義は以下の通りです.

instance Functor Maybe wherefmap _ Nothing = Nothingfmap f (Just a) = Just (f a)

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-652)Maybe aについてNothingの場合とJust aの場合のそれぞれでFunctor則を満たすことを確認すれば良いです.

Kinebuchi Tomohiko 圏とHaskellの型

Page 29: 圏とHaskellの型

[プ] MaybeがFunctor則を満たすこと (2/2)

fmap id Nothing == Nothingfmap id (Just a) == Just (id a) == Just a

fmap (g . f) Nothing == Nothing((fmap g) . (fmap f)) Nothing== fmap g (fmap f Nothing)== fmap g Nothing== Nothing

fmap (g . f) (Just a) == Just ((g . f) a)== Just (g (f a))

((fmap g) . (fmap f)) (Just a)== fmap g (fmap f (Just a))== fmap g (Just (f a))== Just (g (f a))

Kinebuchi Tomohiko 圏とHaskellの型

Page 30: 圏とHaskellの型

[プ][圏] Maybeで関数を写してみよう

例: 整数の値を取って, その文字列表現を返す関数int2str.

int2str :: Integer -> Stringint2str i = show i

この関数をMaybeで写すと,

fmap int2str :: Maybe Integer -> Maybe String

という関数になります.

Hask圏 Hask圏

Integerint2str// String

Maybe +3 Maybe Integerfmap int2str// Maybe String

Kinebuchi Tomohiko 圏とHaskellの型

Page 31: 圏とHaskellの型

[プ] ghciで動かしてみる

$ ghciPrelude> :load int2str.hsPrelude> :t int2strint2str :: Integer -> String

Prelude> :t fmap int2strfmap int2str :: Functor f => f Integer -> f String

Prelude> int2str 1"1"Prelude> fmap int2str (Just 3)Just "3"Prelude> fmap int2str NothingNothing

Kinebuchi Tomohiko 圏とHaskellの型

Page 32: 圏とHaskellの型

[プ] Functorの例: List

Listとは, ある型の値を0個以上並べたものの型です. 定義は以下の通りです

data [] a = [] | a : [a]

(https://hackage.haskell.org/package/ghc-prim-0.4.0.0/candidate/docs/src/GHC-Types.html#line-33)右辺の[]が空のリストを表し,a : [a]がリストの前に要素を1つ追加したものを表します.リストの定義では再帰的な定義が使われています.

Kinebuchi Tomohiko 圏とHaskellの型

Page 33: 圏とHaskellの型

[プ] ListがFunctor則を満たすこと (1/3)

ListのFunctorとしての定義は以下の通りです.

-- The list type

instance Functor [] wherefmap = map

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-732)

Kinebuchi Tomohiko 圏とHaskellの型

Page 34: 圏とHaskellの型

[プ] ListがFunctor則を満たすこと (2/3)

FunctorとしてのListの定義に出てきた map関数の定義は以下の通りです.

map :: (a -> b) -> [a] -> [b]map _ [] = []map f (x:xs) = f x : map f xs

(https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-868)Listについて[]の場合とa : [a]の場合のそれぞれで Functor則を満たすことを確認すれば良いです.

Kinebuchi Tomohiko 圏とHaskellの型

Page 35: 圏とHaskellの型

[プ] ListがFunctor則を満たすこと (3/3)

fmap id [] == []fmap id (x:xs) == id x : map id xs == x : map id xs

fmap (g . f) [] == []((fmap g) . (fmap f)) []== fmap g (fmap f [])== fmap g [] == []

fmap (g . f) (x:xs) == (g . f) x : map (g . f) xs== g (f x) : map (g . f) xs

((fmap g) . (fmap f)) (x:xs)== fmap g (fmap f (x:xs))== fmap g (f x : map f xs)== g (f x) : (map g) ((map f) xs)== g (f x) : ((map g) . (map f) xs)== g (f x) : map (g . f) xs

Kinebuchi Tomohiko 圏とHaskellの型

Page 36: 圏とHaskellの型

[プ][圏] Listで関数を写してみよう

例: 整数の値を取って, その文字列表現を返す関数int2str.

int2str :: Integer -> Stringint2str i = show i

この関数をListで写すと,

fmap int2str :: [Integer] -> [String]

という関数になります.

Hask圏 Hask圏

Integerint2str// String

List +3 [Integer]fmap int2str// [String]

Kinebuchi Tomohiko 圏とHaskellの型

Page 37: 圏とHaskellの型

[プ] ghciで動かしてみる

$ ghciPrelude> :load int2str.hsPrelude> :t int2strint2str :: Integer -> String

Prelude> :t fmap int2strfmap int2str :: Functor f => f Integer -> f String

Prelude> int2str 1"1"Prelude> fmap int2str [1, 2, 3]["1","2","3"]Prelude> fmap int2str [][]

Kinebuchi Tomohiko 圏とHaskellの型

Page 38: 圏とHaskellの型

[プ][圏] ここまでの内容で質疑応答

「あのスライドをもう1回見せてほしい」

「あそこの解説を聞き逃した」

「あれってどういう意味?」

Kinebuchi Tomohiko 圏とHaskellの型

Page 39: 圏とHaskellの型

[圏] 自然変換の復習

圏C, Dと関手F,G : C → Dがあったとき, 自然変換α : F → Gとは射の集まりαX : FX → GX (X ∈ Ob(C))であって,∀f : X → Yに対しαY ◦ Ff = Gf ◦ αXを満たすもの

Xf // Y FX

Ff //

αX

��

FY

αY

��GX

Gf // GY

→ さてHask圏で自然変換になるものは何があるでしょう?

Kinebuchi Tomohiko 圏とHaskellの型

Page 40: 圏とHaskellの型

[プ] 例. リストの先頭の要素を取り出す関数

リストの1つ目の要素を取り出す関数takeFirstを次のように定義します. リストが空だった場合はNothingを返しておきます.

takeFirst :: [a] -> Maybe atakeFirst [] = NothingtakeFirst (x:xs) = Just x

プログラミングに出てくる自然変換では, 型変数aが何かには依存しない場合が多いです. (むしろ依存されると気持ち悪いです.)

Kinebuchi Tomohiko 圏とHaskellの型

Page 41: 圏とHaskellの型

[プ] takeFirstは自然変換

takeFirstは自然変換なので,

整数のリストからそれぞれの要素の文字列表現のリストを作り, その先頭の要素を取り出す処理 (→↓のルート)

整数のリストの先頭の要素を取り出して, 整数をその文字列表現に変換する処理 (↓→のルート)

が同じ結果になります.

[Integer]fmap int2str //

takeFirst

��

[String]

takeFirst

��Maybe Integer

fmap int2str // Maybe String

Kinebuchi Tomohiko 圏とHaskellの型

Page 42: 圏とHaskellの型

[プ] 具体的な値でtakeFirstの動きを見ましょう

[1, 2, 3] � fmap int2str //_

takeFirst

��

["1","2","3"]_

takeFirst

��Just 1 � fmap int2str // Just "1"

[] � fmap int2str //_

takeFirst

��

[]_

takeFirst

��Nothing � fmap int2str // Nothing

Kinebuchi Tomohiko 圏とHaskellの型

Page 43: 圏とHaskellの型

参考文献

『すごいHaskellたのしく学ぼう!』(Miran Lipovaca・著/田中英行・村主崇行・共訳)

WikiBooks「Haskell/Category theory」 https://en.wikibooks.org/wiki/Haskell/Category theory

Hackage https://hackage.haskell.org/

『圏論の基礎』(S.マックレーン著, 三好博之/高木理訳)

『Types and Programming Languages』(Benjamin C. Pierce)

『型システム入門』(住井英二郎監訳, 遠藤侑介・酒井政裕・今井敬吾・黒木裕介・今井宜洋・才川隆文・今井健男共訳)

Kinebuchi Tomohiko 圏とHaskellの型

Page 44: 圏とHaskellの型

数学茶屋

「フリートーク中心の数学好きの集まり」

和コンセプト

時期は9月

場所は渋谷近辺

人数は20人くらい

Facebook → https://www.facebook.com/MathTeaHouse

Kinebuchi Tomohiko 圏とHaskellの型

Page 45: 圏とHaskellの型

[プ][圏] おしまい

Haskellの型と圏論の概念の対応 (再掲)Haskellの型システム 圏論の概念

型 対象

関数 射

tuple型 直積

Either型 直和

Functor 関手

Listのtakeなど 自然変換

Kinebuchi Tomohiko 圏とHaskellの型