狠狠撸

狠狠撸Share a Scribd company logo
関数型プログラミングとモナドの
考え方
Masayuki Isobe
isobe@fivesolutions.jp
ファイブソリューションズ 社内勉強会 #4
計算とは何か?
?関数y=f(x)を適用すること。xからyを「計算」する。
f(x)x y
計算
?あらかじめ決められた規則に従って状態を変えること。
“123”“456”
“123456”
計算の手段
?表を用意して、「入力」と「出力あるいは結果」の
全パターンを用意する
入力 状態 出力(結果状態)
1 1 2
2 1 3
3 1 4
… … …
パターンの数が無限にあるので用意できない。
?そこで、「演算」を定義して、「演算」の合成によって
「計算」をくみ上げる。(※演算自体は有限の表で定義)
「計算」=「計画された(一連の)演算」
ここまでのまとめ & この先の議題
入力
演算 出力
状態
計算
?複数の演算をどう合成して関数にする?
?入力に対してどう関数を選ぶのか?
?状態とは何か?
関数
複数の演算をどう合成して関数にする?
? 合成のパターンを用意しておく
演算
演算
演算
入力
???
出力
演算
演算
演算
入力
出力
合成
手段
合成
手段
合成手段の例
? 演算の合成
– y = f(x), z=g(y) ? z = (f ○ g)(x)
? 繰り返し適用
– z = fold(3,f,x) ? z = f(f(f(x)))
– z = map(f,[x,y,z]) ? z = [f(x),f(y),f(z)]
? ちなみに分解手段もある(例:カリー化)
– z = f(x,y) ? z = fc(x)(y)
? 「演算」と「合成?分解手段(=高階関数)」
によって割と自由にプログラミングできる!
入力に対して関数を選ぶ方法
? 多相型(ポリモーフィズム)
入力(x,y)
関数g(x,y) 演算h(x,y) 演算q(x,y)
f(x,y) ? 選択機構
入力(x,y) 関数f(x,y)
fという関数の定義域を広げたい。それができると便利。
(汎用的な道具のほうが、特別なケースでしか使えない道具より便利。
ただし、汎用的なぶんパフォーマンスを犠牲にするので、工夫が必要。)
多相データ型 と 型クラス
?多相データ型: コンテナ型のようなもの。Enumのような定数値も含む。
単純な例) リスト a
aにはintとかfloatとかcharとか、あるいはリストのリストならば 「a = リスト b」となる。
再帰構造の例) 2分木ツリー a
aの型を要素に持つ2分木ツリーコンテナのようなもの。
雑多な例) くじ引き型 a b
aは当選商品のEnum型、bはハズレくじを集めて再チャンス応募の商品のEnum型
?型クラス: 通常のデータ型や多相データ型に対して、適用可能な関数を
保証する型
例) 合計を計算できる型クラス(CanTotal)というのを定義してみる
クラス CanTotal
定義:
関数 total(x) を持つこと
型クラスのインスタンスによるオーバーロード
入力(x)
リスト total(x,y) 二分木 total(x) 他 total(x)
total(x) ? 選択機構
?型クラスのインスタンス: あるデータ型(基本型 or データ多相型)がその
型クラスが規定する関数の定義を持つこと
例1) リスト a 型はCanTotal型クラスのインスタンスであると定義できる。
定義: 引数xをリストa型のデータとしたとき、
total(x) = xに含まれるa型の要素を合計する
例2) 二分木a型はCanTotal型クラスのインスタンスであると定義できる。
定義: 引数xを二分木a型のデータとしたとき、
total(x) = xに含まれるa型の要素を合計する
? これで、totalという関数は、上図のように選択機構がうまく働く。
(オーバーロード または アドホック多相 と呼ぶ)
状態とは何か?
?入力とは別に、関数の引数には現れない状態を扱いたい。
C++やPythonだったら、グローバル変数とか、クラスのメソッドならメンバ変数とかが
「状態」の例
?関数型プログラミング言語はHaskellのように「純粋関数型」とLispやMLやF#のように
そうでない言語があり、「純粋関数型」は「関数の引数には現れない状態」を許さない。
? でも、Haskellでも「状態が関数の外にあるっぽい書き方」をしたい!
状態
入力 関数
暗黙入力 副作用
出力
この構造をどうバラせば、いいだろうか?
暗黙状態アクセスから明示適用部分を分離
3つにバラしてみる(一見ヘンだけど先人たちが色々考えた結果のベストプラクティス)
副作用の予約
メモ
関数
出力
状態からの
切り出し
入力
状態
①状態から「使うところ」
を切り出してくるロジック
③「副作用予約メモ」
を実際に状態に適用
するロジック
②切り出した状態を
入力に加えて明示的に
関数の入力に与え、
状態に作用させるための
情報を得る。
暗黙入力なし。
全て明示的。
出力も状態を経由して一本化
計算本体とバインド関数
前ページの要素を整理すると、さらに「②」と「①、③」にまとめられる
入力 関数
副作用の
予約メモ
②計算本体
①③状態切り出しと、副作用のフィードバック
副作用の予約
メモ状態からの
切り出し
状態
これを
bind(バインド)関数
と呼ぶ
型クラス「モナド」による「状態」と「バインド関数」の対応づけ
入力 関数
副作用の
予約メモ
状態
型を一致
させておく
その型に
Bind関数を
定義しておく
型クラス「モナド」の定義を、
関数「bind」を持つこと、とする
「状態」の型はモナド型クラスの
インスタンスとなる
これで、関数の「入力側」としてはあたかも状態が外部に存在せず
全ての情報が引数として渡され、「出力側」は入力データに関係する
状態更新部分だけ出力するように関数を定義することができ、状態全体
をケアする部分は全てbindに押し付けることができる。
(実演) GHC Preludeで
あとは各自で興味あればぜひ
ここまでコンセプトが分かればもう簡単

More Related Content

関数型プログラミングとモナド