SlideShare a Scribd company logo
1 of 124
Download to read offline
Freer Monads, More
Extensible Effects
PPL 2016

岡山県玉野市 2016/03/08
Oleg Kiselyov
Tohoku University
Hiromi ISHII
University of Tsukuba
(Originally Published in Haskell '15)
(This slide is available at: http://bit.ly/1LaPQrj)
Table of Contents
• Background: Monad Transformer
• Extensible Effects
• More Extensible Effects
• Examples
• Benchmarks
• Conclusions
Background
Background
モナド:関数型言語で命令的に副作用を記述する方法の一つ
Background
•問題:モナド(=副作用)の合成
モナド:関数型言語で命令的に副作用を記述する方法の一つ
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
モナド:関数型言語で命令的に副作用を記述する方法の一つ
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
• 主流:モナド変換子 (MTL)
モナド:関数型言語で命令的に副作用を記述する方法の一つ
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
• 主流:モナド変換子 (MTL)
• 各副作用に特化した層を重ねる
モナド:関数型言語で命令的に副作用を記述する方法の一つ
ExceptT String
StateT Int
WriterT [Log]
IO
runExceptT
runStateT
runWriterT
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
• 主流:モナド変換子 (MTL)
• 各副作用に特化した層を重ねる
• ハンドラが担当する副作用を処理して下位層の効果
に変換される
モナド:関数型言語で命令的に副作用を記述する方法の一つ
ExceptT String
StateT Int
WriterT [Log]
IO
runExceptT
runStateT
runWriterT
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
• 主流:モナド変換子 (MTL)
• 各副作用に特化した層を重ねる
• ハンドラが担当する副作用を処理して下位層の効果
に変換される
• 下位層の副作用は lift か型クラスを用いて呼び出す
モナド:関数型言語で命令的に副作用を記述する方法の一つ
ExceptT String
StateT Int
WriterT [Log]
IO
runExceptT
runStateT
runWriterT
Background
•問題:モナド(=副作用)の合成
• 出来合いの物を合成し新たなモナドを創りたい
• 主流:モナド変換子 (MTL)
• 各副作用に特化した層を重ねる
• ハンドラが担当する副作用を処理して下位層の効果
に変換される
• 下位層の副作用は lift か型クラスを用いて呼び出す
★ (More) Extensible Effects はその代替
モナド:関数型言語で命令的に副作用を記述する方法の一つ
ExceptT String
StateT Int
WriterT [Log]
IO
runExceptT
runStateT
runWriterT
モナド変換子の問題点
1. 意味論の固定
• 一つのモナド層に一つの解釈
2. Too many lifts!!!
• 合成が深くなると lift が増える
• 型クラスを使うと同種の副作用の使用に
制限
3. 合成順の固定
• 実行時に合成順が確定され、階層を跨い
だ処理が不可能
4. 合成のコスト
➡ Extensible Effects [KSS2013] はこれらを解決
モナド変換子の問題点
1. 意味論の固定
• 一つのモナド層に一つの解釈
2. Too many lifts!!!
• 合成が深くなると lift が増える
• 型クラスを使うと同種の副作用の使用に
制限
3. 合成順の固定
• 実行時に合成順が確定され、階層を跨い
だ処理が不可能
4. 合成のコスト
➡ Extensible Effects [KSS2013] はこれらを解決
ExceptT String
StateT Int
WriterT [Log]
IO
runExceptT
runStateT
runWriterT
Extensible Effects
Extensible Effects
• アイデア:函手 (Functor) の合成は簡単
Extensible Effects
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
Extensible Effects
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
Extensible Effects
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Extensible Effects
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Request = 命令 (get, tell, throw...)
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Request = 命令 (get, tell, throw...)
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
委譲
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Request = 命令 (get, tell, throw...)
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
委譲 委譲
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
Request = 命令 (get, tell, throw...)
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
委譲 委譲 委譲
• アイデア:函手 (Functor) の合成は簡単
• モナド = join :: m (m a) → m a と return を持つ Functor
• 副作用を函手の直和で表し、その生成する自由モナドを考える
• 副作用の処理をClient-Server モデルで捉える
➡ lift が不要、階層を跨いだ処理が可能(階層がない)
Request = 命令 (get, tell, throw...)
Extensible Effects
Eff [...] aClient = プログラム
runStaterunWriter runErrorcatch
Server = ハンドラ
委譲 委譲 委譲
自由モナド
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
• 自由モナド:函手 f を命令とする抽象
構文木全体
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
ϕ
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
• 自由モナド:函手 f を命令とする抽象
構文木全体
• 函手 f を「含む」最小のモナド
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
ϕ
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
• 自由モナド:函手 f を命令とする抽象
構文木全体
• 函手 f を「含む」最小のモナド
• 普遍性は f の解釈 φ : ∀α. f α → m α から再
帰的にインタプリタ handle φ : ∀α. Free f
α → m α が定まる事に対応
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
ϕ
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
• 自由モナド:函手 f を命令とする抽象
構文木全体
• 函手 f を「含む」最小のモナド
• 普遍性は f の解釈 φ : ∀α. f α → m α から再
帰的にインタプリタ handle φ : ∀α. Free f
α → m α が定まる事に対応
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
f の各命令の解釈
ϕ
∃!
ϕ
ϕ
∃!
ϕ
自由モナド
• クリーネ閉包 Λ★
:Λの有限文字列全体
• 代数的には Λ 上の自由モノイドに相当
• モノイド準同型 φ : Λ → M に対し foldMap
φ : Λ★
→ M は右図を可換にする唯一の写像
• Λ を「含む」最小のモノイド構造
• 自由モナド:函手 f を命令とする抽象
構文木全体
• 函手 f を「含む」最小のモナド
• 普遍性は f の解釈 φ : ∀α. f α → m α から再
帰的にインタプリタ handle φ : ∀α. Free f
α → m α が定まる事に対応
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
S
Λ
ϕ
{ · }
∃!
ϕ
Λ∗
M
Λ
ϕ
{ · }
∃!
ϕ
f の各命令の解釈
φ の決定する

インタプリタ
ϕ
∃!
ϕ
ϕ
∃!
ϕ
ϕ
∃!
ϕ
自由モナド
data Free f α = Pure α
| Join (f (Free f α))
deriving (Functor)
handle' ∷ Functor f ⇒ (f a → a) → Free f a → a
liftF ∷ Functor f ⇒ f a → Free f a
instance (Functor f) ⇒ Monad (Free f) where
return = Pure
Pure a ≫= f = f a
Join mma ≫= f = Join (fmap (≫= f) mma)
自由モナド
• 抽象構文木:モナド = 函手 + join + return
data Free f α = Pure α
| Join (f (Free f α))
deriving (Functor)
handle' ∷ Functor f ⇒ (f a → a) → Free f a → a
liftF ∷ Functor f ⇒ f a → Free f a
instance (Functor f) ⇒ Monad (Free f) where
return = Pure
Pure a ≫= f = f a
Join mma ≫= f = Join (fmap (≫= f) mma)
自由モナド
• 抽象構文木:モナド = 函手 + join + return
• Free f はこれらを頂点に持つ木構造として定義出来る
data Free f α = Pure α
| Join (f (Free f α))
deriving (Functor)
handle' ∷ Functor f ⇒ (f a → a) → Free f a → a
liftF ∷ Functor f ⇒ f a → Free f a
instance (Functor f) ⇒ Monad (Free f) where
return = Pure
Pure a ≫= f = f a
Join mma ≫= f = Join (fmap (≫= f) mma)
自由モナド
• 抽象構文木:モナド = 函手 + join + return
• Free f はこれらを頂点に持つ木構造として定義出来る
• Monad の定義は型をグッと睨めば導出できる
data Free f α = Pure α
| Join (f (Free f α))
deriving (Functor)
handle' ∷ Functor f ⇒ (f a → a) → Free f a → a
liftF ∷ Functor f ⇒ f a → Free f a
instance (Functor f) ⇒ Monad (Free f) where
return = Pure
Pure a ≫= f = f a
Join mma ≫= f = Join (fmap (≫= f) mma)
Reader(環境モナド)
• 函手にするため副作用を継続渡し形式 (CPS) で記述
• 複数の解釈が可能(自由モナドの普遍性)
data Ask i a = Ask (i → a) deriving (Functor)
ask ∷ Free (Ask e) e
ask = liftF (Ask id)
runReader ∷ e → Free (Ask e) a → a
runReader e = handle (λ (Ask k) → k e)
runIt ∷ [e] → Free (Ask e) a → a
runIt _ (Pure a) = a
runIt (x : xs) (Join (Ask k)) = runIt xs (k x)
Reader(環境モナド)
• 函手にするため副作用を継続渡し形式 (CPS) で記述
• 複数の解釈が可能(自由モナドの普遍性)
data Ask i a = Ask (i → a) deriving (Functor)
ask ∷ Free (Ask e) e
ask = liftF (Ask id)
runReader ∷ e → Free (Ask e) a → a
runReader e = handle (λ (Ask k) → k e)
runIt ∷ [e] → Free (Ask e) a → a
runIt _ (Pure a) = a
runIt (x : xs) (Join (Ask k)) = runIt xs (k x)
継続
複数の解釈が可能
ghci> runReader 12 $ ask >> (,) <$> ask <*> ask
⇒ (12,12)
ghci> runIt [12, 23, 34] $ ask >> (,) <$> ask <*> ask
⇒ (23,34)
直和:State = Ask + Tell
直和:State = Ask + Tell
data Tell e a = Tell a e deriving (Functor)
• Writer も同様に継続渡しで作れる
直和:State = Ask + Tell
data Tell e a = Tell a e deriving (Functor)
継続
• Writer も同様に継続渡しで作れる
直和:State = Ask + Tell
data Tell e a = Tell a e deriving (Functor)
継続 ログ出力
• Writer も同様に継続渡しで作れる
直和:State = Ask + Tell
• Tell と Ask で State を創るには?
data Tell e a = Tell a e deriving (Functor)
継続 ログ出力
• Writer も同様に継続渡しで作れる
直和:State = Ask + Tell
• Tell と Ask で State を創るには?
➡ 函手の直和 f ⊕ g:f, g いずれかの構築子を持つ函手
data Tell e a = Tell a e deriving (Functor)
継続 ログ出力
• Writer も同様に継続渡しで作れる
data (f ⊕ g) a = InL (f a)
| InR (g a) deriving (Functor)
runState ∷ s → Free (Tell s ⊕ Ask s) a → (a, s)
runState _ (Join (InL (Tell fa e))) = runState e fa
runState s0 (Join (InR (Ask k))) = runState s0 (k s0)
直和:State = Ask + Tell
• Tell と Ask で State を創るには?
➡ 函手の直和 f ⊕ g:f, g いずれかの構築子を持つ函手
data Tell e a = Tell a e deriving (Functor)
継続 ログ出力
• Writer も同様に継続渡しで作れる
data (f ⊕ g) a = InL (f a)
| InR (g a) deriving (Functor)
runState ∷ s → Free (Tell s ⊕ Ask s) a → (a, s)
runState _ (Join (InL (Tell fa e))) = runState e fa
runState s0 (Join (InR (Ask k))) = runState s0 (k s0)
• 更に複数の効果を合成するには、多項和に一般化すればよい
Open Union
• Open Union: 函手の合成を任意個に一般化
data Union (fs ∷ [★ → ★]) a
class f ∈ fs -- 「f は函手のリスト fs の要素」
inj ∷ f ∈ fs ⇒ f a → Union fs a -- InL, InR に対応
prj ∷ f ∈ fs ⇒ Union fs a → Maybe (f a)
decomp ∷ Union (f : fs) a → Either (Union fs a) (f a)
weaken ∷ Union fs a → Union (f : fs) a
absurd ∷ Union '[] a → b
• 旧 ExtEff [KSS13] では動的キャストを使用
完成:Extensible Effects
• 原論文では、これに更にCPS変換を施している
• モナド変換子の問題のうち、以下は解決
1. 意味論の固定 2. Too many lifts!!! 3. 合成順の固定
๏ 3 については後ほど More の章で詳説
newtype Eff r a = Eff { unEff ∷ Free (Union r) a }
deriving (Functor, Monad, Applicative)
Extensible Effects まとめ
Extensible Effects まとめ
Extensible Effects
‖
Free Monad
+
Open Union
+
継続渡し形式(CPS)
Ext Eff の問題点
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
• ライブラリ開発が面倒
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
• ライブラリ開発が面倒
• 技術的な問題:動的キャストの利用
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
• ライブラリ開発が面倒
• 技術的な問題:動的キャストの利用
• 実行時コストが嵩む
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
• ライブラリ開発が面倒
• 技術的な問題:動的キャストの利用
• 実行時コストが嵩む
• 基底モナドに ST s などSkolem変数を含む型が使えない
Ext Eff の問題点
• 合成に Functor が必要、fmap にコスト
• 自由モナドは効率が悪く、reflectionにも不向き
• 継続ベースで、ハンドラが書きづらい
• ライブラリ開発が面倒
• 技術的な問題:動的キャストの利用
• 実行時コストが嵩む
• 基底モナドに ST s などSkolem変数を含む型が使えない
★ More Extensible Effects (提案手法)はこれらを解決
Freer Monads, 

More Extensible Effects
Extensible Effects
Extensible Effects
‖
Free Monad
+
Open Union
+
継続渡し形式(CPS)
More

Extensible Effects
More Extensible Effects
‖
Freer Monad
+
Open Union
+
Type-aligned Sequence
= Operational
Monad
Freer Monad とは
Freer Monad とは
• ??「Freer Monad は単項型の恒等函手に沿っ

   た左 Kan 拡張だよ。何か問題でも?」
Freer Monad とは
• ??「Freer Monad は単項型の恒等函手に沿っ

   た左 Kan 拡張だよ。何か問題でも?」
➡ 問題しかない(少なくとも私にとっては)
Freer Monad とは
• ??「Freer Monad は単項型の恒等函手に沿っ

   た左 Kan 拡張だよ。何か問題でも?」
➡ 問題しかない(少なくとも私にとっては)
• これまでを踏まえた説明:
Freer Monad とは
• ??「Freer Monad は単項型の恒等函手に沿っ

   た左 Kan 拡張だよ。何か問題でも?」
➡ 問題しかない(少なくとも私にとっては)
• これまでを踏まえた説明:
• Freer Monad = 自由 Functor + 自由モナド
自由 Functor と

Freer Monad
data FFree f a = ∀ b. FMap (b → a) (f b)
instance Functor (FFree f) where
fmap f (FMap g a) = FMap (f ◦ g) a
type Freer f a = Free (FFree f) a
自由 Functor と

Freer Monad
data FFree f a = ∀ b. FMap (b → a) (f b)
instance Functor (FFree f) where
fmap f (FMap g a) = FMap (f ◦ g) a
type Freer f a = Free (FFree f) a
fmap !
自由 Functor と

Freer Monad
• 任意の単項型構築子 f ∷ ★ → ★ に対し、FFree f ∷ ★ → ★
は自動的にFunctor (= 自由Functor = Idに沿った左Kan拡張)
data FFree f a = ∀ b. FMap (b → a) (f b)
instance Functor (FFree f) where
fmap f (FMap g a) = FMap (f ◦ g) a
type Freer f a = Free (FFree f) a
fmap !
自由 Functor と

Freer Monad
• 任意の単項型構築子 f ∷ ★ → ★ に対し、FFree f ∷ ★ → ★
は自動的にFunctor (= 自由Functor = Idに沿った左Kan拡張)
data FFree f a = ∀ b. FMap (b → a) (f b)
instance Functor (FFree f) where
fmap f (FMap g a) = FMap (f ◦ g) a
type Freer f a = Free (FFree f) a
➡ FFree と Free で Functorを仮定せずにモナドが出来る
Freer の定義の導出
Freer の定義の導出
• Free と FFree の定義を展開する
Freer f a = Free (FFree f) a
≃ Pure a
| Impure (FFree f (Freer f a))
≃ Pure a
| ∀ b. Impure (f b) (b → Freer f a)
Freer の定義の導出
• Free と FFree の定義を展開する
Freer f a = Free (FFree f) a
≃ Pure a
| Impure (FFree f (Freer f a))
≃ Pure a
| ∀ b. Impure (f b) (b → Freer f a)
• f を OpenUnionで置き換えれば:
data Eff r a
= Pure a
| ∀ b. Impure (Union r b) (b → Eff r a)
Freer の定義の導出
• Free と FFree の定義を展開する
Freer f a = Free (FFree f) a
≃ Pure a
| Impure (FFree f (Freer f a))
≃ Pure a
| ∀ b. Impure (f b) (b → Freer f a)
• f を OpenUnionで置き換えれば:
data Eff r a
= Pure a
| ∀ b. Impure (Union r b) (b → Eff r a)
return
Freer の定義の導出
• Free と FFree の定義を展開する
Freer f a = Free (FFree f) a
≃ Pure a
| Impure (FFree f (Freer f a))
≃ Pure a
| ∀ b. Impure (f b) (b → Freer f a)
• f を OpenUnionで置き換えれば:
data Eff r a
= Pure a
| ∀ b. Impure (Union r b) (b → Eff r a)
return
bind (>>=)!
インスタンス定義
• 継続を合成していけば良いだけの素直な実装
• Functor 不要!(fmap f a = (return . f) =<< a)
data Eff r a
= Pure a
| ∀ b. Impure (Union r b) (b → Eff r a)
instance Monad (Eff f) where
return = Pure
(Pure a) ≫= f = f a
(Impure fa k) ≫= f = Impure fa (k >=> f)
ハンドラ実装用便利関数
• 古い ExtEff だとここまで使い易い型にならない
• リレーしていくだけなのでこれらの実装も容易
• ループ内部状態ありの版や、効果を取り除かない版も可能
handle_relay ∷ (a → Eff r w)
→ (∀ v. t v → (v → Eff r w) → Eff r w)
→ Eff (t ': r) a → Eff r w
handle_relay ret h m = ...
send ∷ (t ∈ r) ⇒ t v → Eff r v
send cmd = Impure (inj cmd) Pure
return !
(>>=) !
例:Ask, Tell 再訪
data Ask i a where Ask ∷ Ask i i
data Tell i a where Tell ∷ i → Tell i ()
ask ∷ (Ask i ∈ r) ⇒ Eff r i
ask = send Ask
tell ∷ (Tell i ∈ r) ⇒ i → Eff r ()
tell = send ◦ Tell
例:Ask, Tell 再訪
• プリミティヴな関数とほぼ同じ型のデータ構築子を用意
data Ask i a where Ask ∷ Ask i i
data Tell i a where Tell ∷ i → Tell i ()
ask ∷ (Ask i ∈ r) ⇒ Eff r i
ask = send Ask
tell ∷ (Tell i ∈ r) ⇒ i → Eff r ()
tell = send ◦ Tell
例:Ask, Tell 再訪
• プリミティヴな関数とほぼ同じ型のデータ構築子を用意
➡ 旧来の ExtEff での定義に比べて非常に素直
data Ask i a where Ask ∷ Ask i i
data Tell i a where Tell ∷ i → Tell i ()
ask ∷ (Ask i ∈ r) ⇒ Eff r i
ask = send Ask
tell ∷ (Tell i ∈ r) ⇒ i → Eff r ()
tell = send ◦ Tell
ハンドラの実装
• 実質 return と (>>=) を書くだけなので楽
runReader ∷ e → Eff (Ask e : r) a → Eff r a
runReader e = handle_relay return (λ Ask arr → arr e)
runIt :: [e] → Eff (Ask e : r) a → Eff r a
runIt = handle_relay_s (λ _ a → return a) $
λ (x : xs) Ask arr → arr xs x
runWriter ∷ Eff (Tell e : r) a → Eff r (a, [e])
runWriter = handle_relay (λa → return (a, [])) $
λ (Tell e) f → do { (a, es) ← f () ; return (a, e:es) }
More

Extensible Effects
More Extensible Effects
‖
Freer Monad
+
Open Union
+
Type-aligned Sequence
= Operational
Monad
More

Extensible Effects
More Extensible Effects
‖
Freer Monad
+
Open Union
+
Type-aligned Sequence
これまでの実装の欠点
• Eff モナドインスタンスの定義
instance Monad (Eff f) where
return = Pure
(Pure x) ≫= f = f x
(Impure fa k) ≫= f = Impure fa (k >=> f)
これまでの実装の欠点
• Eff モナドインスタンスの定義
instance Monad (Eff f) where
return = Pure
(Pure x) ≫= f = f x
(Impure fa k) ≫= f = Impure fa (k >=> f)
• 継続が第二引数に左結合的に蓄積されていく
これまでの実装の欠点
• Eff モナドインスタンスの定義
instance Monad (Eff f) where
return = Pure
(Pure x) ≫= f = f x
(Impure fa k) ≫= f = Impure fa (k >=> f)
• 継続が第二引数に左結合的に蓄積されていく
• ハンドラは命令列を頭から逐次実行する
• 左結合だと、最初の数個だけが必要でも継続全体を走査する
必要がある!
これまでの実装の欠点
• Eff モナドインスタンスの定義
instance Monad (Eff f) where
return = Pure
(Pure x) ≫= f = f x
(Impure fa k) ≫= f = Impure fa (k >=> f)
• 継続が第二引数に左結合的に蓄積されていく
• ハンドラは命令列を頭から逐次実行する
• 左結合だと、最初の数個だけが必要でも継続全体を走査する
必要がある!
➡ 解決策:Type-aligned Sequence [PK14]
Type-aligned Sequence
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
• 初出では Reflection w/o Remorse そのものを使っていた
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
• 初出では Reflection w/o Remorse そのものを使っていた
type FTCQ (m ∷ ★ → ★) a b
tsingleton ∷ (a → m b) → FTCQ m a b
(▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b
(⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b
data ViewL m a b where
TOne ∷ (a → m b) → ViewL m a b
(:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b
tviewl ∷ FTCQ m a b → ViewL m a b
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
• 初出では Reflection w/o Remorse そのものを使っていた
type FTCQ (m ∷ ★ → ★) a b
tsingleton ∷ (a → m b) → FTCQ m a b
(▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b
(⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b
data ViewL m a b where
TOne ∷ (a → m b) → ViewL m a b
(:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b
tviewl ∷ FTCQ m a b → ViewL m a b
a → m b に対応
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
• 初出では Reflection w/o Remorse そのものを使っていた
type FTCQ (m ∷ ★ → ★) a b
tsingleton ∷ (a → m b) → FTCQ m a b
(▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b
(⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b
data ViewL m a b where
TOne ∷ (a → m b) → ViewL m a b
(:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b
tviewl ∷ FTCQ m a b → ViewL m a b
a → m b に対応
(>=>)
Type-aligned Sequence
• 直感:関数を実際に合成する代わりに関数の列を
考えて必要になったら適宜評価していけばよい
• 安価な snoc, cat, uncons を持つ二分木 + 型 Hack
• 初出では Reflection w/o Remorse そのものを使っていた
type FTCQ (m ∷ ★ → ★) a b
tsingleton ∷ (a → m b) → FTCQ m a b
(▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b
(⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b
data ViewL m a b where
TOne ∷ (a → m b) → ViewL m a b
(:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b
tviewl ∷ FTCQ m a b → ViewL m a b
a → m b に対応
(>=>)
uncons
最終結果
• Union と FTCQ の実装を改善していけば、効率
が劇的に改善する
type Arrs r a b = FTCQ (Eff r) a b
data Eff r a = Pure a
| ∀ b. Impure (Union r b) (Arrs r b a)
instance Monad (Eff r) where
return = Pure
(Pure a) ≫= f = f a
(Impure fa k) ≫= f = Impure fa (k ▹ f)
handle_relay ∷ ...
Example
Example : 例外処理
Example : 例外処理
• MTL が最も苦手とする物の一つ
• 「階層を跨いだ処理」が効いてくる場面の一つ
• (以下、わかりやすさのため Refl w/o Remorse
適用前のコード)
例外作用の定義
data Exc e a = ThrowError e
throwError ∷ Exc e ∈ r ⇒ e → Eff r a
catchError ∷ ∀ e r a. Exc e ∈ r

⇒ Eff r a → (e → Eff r a) → Eff r a
catchError act h = interpose return bind act where
bind ∷ Exc e b → (b → Eff r a) → Eff r a
bind (Exc e) _ = h e
runError ∷ Eff (Exc e : r) a → Eff r (Either e a)
runError = handle_relay (return ◦ Right) $
λ(ThrowError e) _ → return (Left e)
例外作用の定義
data Exc e a = ThrowError e
throwError ∷ Exc e ∈ r ⇒ e → Eff r a
catchError ∷ ∀ e r a. Exc e ∈ r

⇒ Eff r a → (e → Eff r a) → Eff r a
catchError act h = interpose return bind act where
bind ∷ Exc e b → (b → Eff r a) → Eff r a
bind (Exc e) _ = h e
runError ∷ Eff (Exc e : r) a → Eff r (Either e a)
runError = handle_relay (return ◦ Right) $
λ(ThrowError e) _ → return (Left e)
• interpose: 作用を除去しないハンドラを書くためのもの
例外作用の定義
data Exc e a = ThrowError e
throwError ∷ Exc e ∈ r ⇒ e → Eff r a
catchError ∷ ∀ e r a. Exc e ∈ r

⇒ Eff r a → (e → Eff r a) → Eff r a
catchError act h = interpose return bind act where
bind ∷ Exc e b → (b → Eff r a) → Eff r a
bind (Exc e) _ = h e
runError ∷ Eff (Exc e : r) a → Eff r (Either e a)
runError = handle_relay (return ◦ Right) $
λ(ThrowError e) _ → return (Left e)
• interpose: 作用を除去しないハンドラを書くためのもの
• 例外があれば対処すれば良いだけなので素直で簡潔
トランザクション
• 例外等で動作が中断した場合、継続は呼ばれない
➡ Pure で末端に達したら状態を大域的に反映すればよい
transaction ∷ ∀ s r a. (State s) ∈ r
⇒ Proxy s → Eff r a → Eff r a
transaction _ act = do s ← get; loop s act where
loop ∷ s → Eff r a → Eff r a
loop s (Pure a) = put s ≫ return a
loop s (Impure (u ∷ Union r b) k) =
case prj u ∷ Maybe (State s b) of
Just Get → loop s $ k s
Just (Put s') → loop s' $ k ()
Nothing → Impure u (loop s ◦ k)
実行例
transTest
= runLift $ flip runState True $
flip runState 'a' $
runError' (Proxy ∷ Proxy String) $ handled $
transaction (Proxy ∷ Proxy Bool) $ do
modify (succ ∷ Char → Char)
modify not
throwError "interrupted!"
where handled = flip catchError $ λ str →
lift (putStrLn ("ignored: " ++ str))
ghci> transTest
ignored: interrupted!
==> ((Right (), 'b'), True)
実行例
transTest
= runLift $ flip runState True $
flip runState 'a' $
runError' (Proxy ∷ Proxy String) $ handled $
transaction (Proxy ∷ Proxy Bool) $ do
modify (succ ∷ Char → Char)
modify not
throwError "interrupted!"
where handled = flip catchError $ λ str →
lift (putStrLn ("ignored: " ++ str))
ghci> transTest
ignored: interrupted!
==> ((Right (), 'b'), True)
Bool の状態のみ

トランザクションで保護
実行例
transTest
= runLift $ flip runState True $
flip runState 'a' $
runError' (Proxy ∷ Proxy String) $ handled $
transaction (Proxy ∷ Proxy Bool) $ do
modify (succ ∷ Char → Char)
modify not
throwError "interrupted!"
where handled = flip catchError $ λ str →
lift (putStrLn ("ignored: " ++ str))
ghci> transTest
ignored: interrupted!
==> ((Right (), 'b'), True)
保護されていたBool

の変更は反映されない
Bool の状態のみ

トランザクションで保護
MTLでは?
• 階層を跨げないので transaction に相当する関数は実装不能
• しかも合成順によって挙動が違う!
• ExceptT が先頭なら全部コミット、そうでなきゃロールバック
ghci> let handler = flip catchError $ λs →
liftIO (putStrLn ("ignored: " ++ s))
ghci> runExceptT $ flip runStateT True $ handler $
(modify not ≫ throwError "hay")
ignored: hay
==> Right ((),True)
ghci> flip runStateT True $ runExceptT $ handler $
(modify not ≫ throwError "hay")
ignored: hay
==> (Right (),False)
まとめ・その他
• (More) Extensible Effects では、階層を跨いだ
ハンドラが実装出来る
• MTL では実現不可能だった処理も実装可能
• I/Oエラーの捕捉や、Error モナドへのリレーも容易
• 原論文 [KI15] には論理モナドやリージョン計算
など、他の例もあり
• More EE それ自体をモナド変換子として用いる
事も可能(基底モナド付き計算)
Benchmarks
Benchmarks
• メイン:5の倍数を数える
• その上下に余分な Reader 層
を足し計算を実行
• MTL、旧ExtEff、MoreEE と
競合手法の HIA [KSS13] を
比較
• 環境:Intel Core i7 2.8GHz,
16GB RAM, GHC 7.10.3.

-threaded -O2 −rtsopts
benchS ns = foldM f 1 ns
where
f acc x | x `mod` 5 == 0 = do
s ← S.get
S.put $! (s+1)
return $! max acc x
f acc x = return $! max acc x
{-# NOINLINE benchS #-}
NOINLINE を付けて大きな計算と見做す
(GHC 7.8 までは付けなくてもMTLが
圧倒的に悪かった)
Readers under State
実行時間(sec)
0
1.25
2.5
3.75
5
0 1 2 3 4
MTL HIA Old ExtEff More ExtEff
TotalAlloc(KBytes)
0
200
400
600
800
0 250 500 750 1000
Reader の数
TotalAlloc(Kbytes)
0
200
400
600
800
0 250 500 750 1000
Reader の数
Readers over State
GHC 7.8 ではより
O(n2) に近い勾配
実行時間(sec)
0
2.25
4.5
6.75
9
0 2 4 6 8
MTL HIA Old ExtEff More ExtEff
補足と考察
• 旧 Ext Eff から大幅に改善
• Reader層を State 層に置き換えると、両方の場
合についてNOINLINE 付きでも MTL が線型速度
• 大きな計算の場合、時間・空間消費量ともに
More Ext Eff が最適、競合手法とも互角
• INLINE が効く小さい計算、あるいは単純な State
/ Reader 計算は最適化が効いて MTL が勝つ
• GHC の最適化機構の進化は凄まじい
Conclusions
Conclusions
• More Extensible Effects は MTL の代替
๏ 高効率
• 時間・空間計算量が、競合手法の中でもかなり効率的
• 多数の副作用を合成する際に威力を発揮
๏ 柔軟で高い表現力
• 制約無しに副作用を合成可能
• 階層を跨いだ処理、複数の解釈を許容
๏ No More Lifts!
参考文献
[KI15] Kiselyov and ISHII, Freer Monads, More Extensible Effects. Haskell '15.
[KLO13] Kammar, S. Lindley, and N. Oury, Handlers in action. ICFP '13.
[KSS13] Kiselyov, Sabry and Swords, Extensible Effects: An Alternative to

Monad Transformers. Haskell '13.
[PK14] van der Ploeg and Kiselyov, Reflection without Remorse: Revealing a

hidden sequence to speed up monadic reflection. Haskell '13.
Thank you!
Any Questions?
• More Extensible Effects は MTL の代替
๏ 高効率
• 時間・空間計算量が、競合手法の中でもかなり効率的
• 多数の副作用を合成する際に威力を発揮
๏ 柔軟で高い表現力
• 制約無しに副作用を合成可能
• 階層を跨いだ処理、複数の解釈を許容
๏ No More Lifts!

More Related Content

What's hot

Haskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのかHaskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのか
Kousuke Ruichi
 
F#のコンピュテーション式
F#のコンピュテーション式F#のコンピュテーション式
F#のコンピュテーション式
pocketberserker
 

What's hot (20)

LR parsing
LR parsingLR parsing
LR parsing
 
「FPGA 開発入門:FPGA を用いたエッジ AI の高速化手法を学ぶ」
「FPGA 開発入門:FPGA を用いたエッジ AI の高速化手法を学ぶ」「FPGA 開発入門:FPGA を用いたエッジ AI の高速化手法を学ぶ」
「FPGA 開発入門:FPGA を用いたエッジ AI の高速化手法を学ぶ」
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!
 
F#によるFunctional Programming入門
F#によるFunctional Programming入門F#によるFunctional Programming入門
F#によるFunctional Programming入門
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPC
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
Msを16倍出し抜くwpf開発2回目
Msを16倍出し抜くwpf開発2回目Msを16倍出し抜くwpf開発2回目
Msを16倍出し抜くwpf開発2回目
 
llvm入門
llvm入門llvm入門
llvm入門
 
Haskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのかHaskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのか
 
ラムダ計算入門
ラムダ計算入門ラムダ計算入門
ラムダ計算入門
 
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
 
F#のコンピュテーション式
F#のコンピュテーション式F#のコンピュテーション式
F#のコンピュテーション式
 
Row-based Effect Systems for Algebraic Effect Handlers
Row-based Effect Systems for Algebraic Effect HandlersRow-based Effect Systems for Algebraic Effect Handlers
Row-based Effect Systems for Algebraic Effect Handlers
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
 
暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
 
アクセスプラン(実行計画)の読み方入門
アクセスプラン(実行計画)の読み方入門アクセスプラン(実行計画)の読み方入門
アクセスプラン(実行計画)の読み方入門
 

Similar to Freer Monads, More Extensible Effects

すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
Nozomu Kaneko
 
すごいHaskell読書会#10
すごいHaskell読書会#10すごいHaskell読書会#10
すごいHaskell読書会#10
Shin Ise
 

Similar to Freer Monads, More Extensible Effects (18)

Haskell Lecture 2
Haskell Lecture 2Haskell Lecture 2
Haskell Lecture 2
 
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
 
Applicative functor
Applicative functorApplicative functor
Applicative functor
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたって
 
MP in Scala
MP in ScalaMP in Scala
MP in Scala
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたって
 
すごいHaskell読書会#10
すごいHaskell読書会#10すごいHaskell読書会#10
すごいHaskell読書会#10
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
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
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
モナドがいっぱい!
モナドがいっぱい!モナドがいっぱい!
モナドがいっぱい!
 
マスターオブゴールーチンアンドチャネル スタートGo #1
マスターオブゴールーチンアンドチャネル   スタートGo #1マスターオブゴールーチンアンドチャネル   スタートGo #1
マスターオブゴールーチンアンドチャネル スタートGo #1
 
MP in Haskell
MP in HaskellMP in Haskell
MP in Haskell
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
 
Chainerの使い方と 自然言語処理への応用
Chainerの使い方と自然言語処理への応用Chainerの使い方と自然言語処理への応用
Chainerの使い方と 自然言語処理への応用
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
 

More from Hiromi Ishii

Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Hiromi Ishii
 

More from Hiromi Ishii (16)

ものまね鳥を愛でる 結合子論理と計算
ものまね鳥を愛でる 結合子論理と計算ものまね鳥を愛でる 結合子論理と計算
ものまね鳥を愛でる 結合子論理と計算
 
Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
Lebesgue可測性に関するSoloayの定理と実数の集合の正則性Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
 
実数の集合はどこまで可測になれるか?
実数の集合はどこまで可測になれるか?実数の集合はどこまで可測になれるか?
実数の集合はどこまで可測になれるか?
 
(数式の入った)本をつくる
(数式の入った)本をつくる(数式の入った)本をつくる
(数式の入った)本をつくる
 
御清聴ありがとうございました
御清聴ありがとうございました御清聴ありがとうございました
御清聴ありがとうございました
 
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
 
技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底
 
Algebraic DP: 動的計画法を書きやすく
Algebraic DP: 動的計画法を書きやすくAlgebraic DP: 動的計画法を書きやすく
Algebraic DP: 動的計画法を書きやすく
 
Yesod でブログエンジンをつくってみた
Yesod でブログエンジンをつくってみたYesod でブログエンジンをつくってみた
Yesod でブログエンジンをつくってみた
 
Yesodを支える技術
Yesodを支える技術Yesodを支える技術
Yesodを支える技術
 
Alloy Analyzer のこと
Alloy Analyzer のことAlloy Analyzer のこと
Alloy Analyzer のこと
 
Invertible-syntax 入門
Invertible-syntax 入門Invertible-syntax 入門
Invertible-syntax 入門
 
最終発表
最終発表最終発表
最終発表
 
Metaprogramming in Haskell
Metaprogramming in HaskellMetaprogramming in Haskell
Metaprogramming in Haskell
 
Template Haskell とか
Template Haskell とかTemplate Haskell とか
Template Haskell とか
 
実践・完全犯罪のつくり方
実践・完全犯罪のつくり方実践・完全犯罪のつくり方
実践・完全犯罪のつくり方
 

Recently uploaded

Recently uploaded (12)

論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 
Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native Integrations
 

Freer Monads, More Extensible Effects

  • 1. Freer Monads, More Extensible Effects PPL 2016
 岡山県玉野市 2016/03/08 Oleg Kiselyov Tohoku University Hiromi ISHII University of Tsukuba (Originally Published in Haskell '15) (This slide is available at: http://bit.ly/1LaPQrj)
  • 2. Table of Contents • Background: Monad Transformer • Extensible Effects • More Extensible Effects • Examples • Benchmarks • Conclusions
  • 8. Background •問題:モナド(=副作用)の合成 • 出来合いの物を合成し新たなモナドを創りたい • 主流:モナド変換子 (MTL) • 各副作用に特化した層を重ねる モナド:関数型言語で命令的に副作用を記述する方法の一つ ExceptT String StateT Int WriterT [Log] IO runExceptT runStateT runWriterT
  • 9. Background •問題:モナド(=副作用)の合成 • 出来合いの物を合成し新たなモナドを創りたい • 主流:モナド変換子 (MTL) • 各副作用に特化した層を重ねる • ハンドラが担当する副作用を処理して下位層の効果 に変換される モナド:関数型言語で命令的に副作用を記述する方法の一つ ExceptT String StateT Int WriterT [Log] IO runExceptT runStateT runWriterT
  • 10. Background •問題:モナド(=副作用)の合成 • 出来合いの物を合成し新たなモナドを創りたい • 主流:モナド変換子 (MTL) • 各副作用に特化した層を重ねる • ハンドラが担当する副作用を処理して下位層の効果 に変換される • 下位層の副作用は lift か型クラスを用いて呼び出す モナド:関数型言語で命令的に副作用を記述する方法の一つ ExceptT String StateT Int WriterT [Log] IO runExceptT runStateT runWriterT
  • 11. Background •問題:モナド(=副作用)の合成 • 出来合いの物を合成し新たなモナドを創りたい • 主流:モナド変換子 (MTL) • 各副作用に特化した層を重ねる • ハンドラが担当する副作用を処理して下位層の効果 に変換される • 下位層の副作用は lift か型クラスを用いて呼び出す ★ (More) Extensible Effects はその代替 モナド:関数型言語で命令的に副作用を記述する方法の一つ ExceptT String StateT Int WriterT [Log] IO runExceptT runStateT runWriterT
  • 12. モナド変換子の問題点 1. 意味論の固定 • 一つのモナド層に一つの解釈 2. Too many lifts!!! • 合成が深くなると lift が増える • 型クラスを使うと同種の副作用の使用に 制限 3. 合成順の固定 • 実行時に合成順が確定され、階層を跨い だ処理が不可能 4. 合成のコスト ➡ Extensible Effects [KSS2013] はこれらを解決
  • 13. モナド変換子の問題点 1. 意味論の固定 • 一つのモナド層に一つの解釈 2. Too many lifts!!! • 合成が深くなると lift が増える • 型クラスを使うと同種の副作用の使用に 制限 3. 合成順の固定 • 実行時に合成順が確定され、階層を跨い だ処理が不可能 4. 合成のコスト ➡ Extensible Effects [KSS2013] はこれらを解決 ExceptT String StateT Int WriterT [Log] IO runExceptT runStateT runWriterT
  • 16. • アイデア:函手 (Functor) の合成は簡単 Extensible Effects
  • 17. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor Extensible Effects
  • 18. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える Extensible Effects
  • 19. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Extensible Effects
  • 20. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ
  • 21. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Request = 命令 (get, tell, throw...) Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ
  • 22. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Request = 命令 (get, tell, throw...) Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ 委譲
  • 23. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Request = 命令 (get, tell, throw...) Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ 委譲 委譲
  • 24. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える Request = 命令 (get, tell, throw...) Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ 委譲 委譲 委譲
  • 25. • アイデア:函手 (Functor) の合成は簡単 • モナド = join :: m (m a) → m a と return を持つ Functor • 副作用を函手の直和で表し、その生成する自由モナドを考える • 副作用の処理をClient-Server モデルで捉える ➡ lift が不要、階層を跨いだ処理が可能(階層がない) Request = 命令 (get, tell, throw...) Extensible Effects Eff [...] aClient = プログラム runStaterunWriter runErrorcatch Server = ハンドラ 委譲 委譲 委譲
  • 28. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 Λ∗ S Λ ϕ { · } ∃! ϕ
  • 29. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 Λ∗ S Λ ϕ { · } ∃! ϕ
  • 30. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ
  • 31. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ
  • 32. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ
  • 33. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 • 自由モナド:函手 f を命令とする抽象 構文木全体 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ ϕ ∃! ϕ
  • 34. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 • 自由モナド:函手 f を命令とする抽象 構文木全体 • 函手 f を「含む」最小のモナド Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ ϕ ∃! ϕ
  • 35. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 • 自由モナド:函手 f を命令とする抽象 構文木全体 • 函手 f を「含む」最小のモナド • 普遍性は f の解釈 φ : ∀α. f α → m α から再 帰的にインタプリタ handle φ : ∀α. Free f α → m α が定まる事に対応 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ ϕ ∃! ϕ
  • 36. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 • 自由モナド:函手 f を命令とする抽象 構文木全体 • 函手 f を「含む」最小のモナド • 普遍性は f の解釈 φ : ∀α. f α → m α から再 帰的にインタプリタ handle φ : ∀α. Free f α → m α が定まる事に対応 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ f の各命令の解釈 ϕ ∃! ϕ ϕ ∃! ϕ
  • 37. 自由モナド • クリーネ閉包 Λ★ :Λの有限文字列全体 • 代数的には Λ 上の自由モノイドに相当 • モノイド準同型 φ : Λ → M に対し foldMap φ : Λ★ → M は右図を可換にする唯一の写像 • Λ を「含む」最小のモノイド構造 • 自由モナド:函手 f を命令とする抽象 構文木全体 • 函手 f を「含む」最小のモナド • 普遍性は f の解釈 φ : ∀α. f α → m α から再 帰的にインタプリタ handle φ : ∀α. Free f α → m α が定まる事に対応 Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ S Λ ϕ { · } ∃! ϕ Λ∗ M Λ ϕ { · } ∃! ϕ f の各命令の解釈 φ の決定する
 インタプリタ ϕ ∃! ϕ ϕ ∃! ϕ ϕ ∃! ϕ
  • 38. 自由モナド data Free f α = Pure α | Join (f (Free f α)) deriving (Functor) handle' ∷ Functor f ⇒ (f a → a) → Free f a → a liftF ∷ Functor f ⇒ f a → Free f a instance (Functor f) ⇒ Monad (Free f) where return = Pure Pure a ≫= f = f a Join mma ≫= f = Join (fmap (≫= f) mma)
  • 39. 自由モナド • 抽象構文木:モナド = 函手 + join + return data Free f α = Pure α | Join (f (Free f α)) deriving (Functor) handle' ∷ Functor f ⇒ (f a → a) → Free f a → a liftF ∷ Functor f ⇒ f a → Free f a instance (Functor f) ⇒ Monad (Free f) where return = Pure Pure a ≫= f = f a Join mma ≫= f = Join (fmap (≫= f) mma)
  • 40. 自由モナド • 抽象構文木:モナド = 函手 + join + return • Free f はこれらを頂点に持つ木構造として定義出来る data Free f α = Pure α | Join (f (Free f α)) deriving (Functor) handle' ∷ Functor f ⇒ (f a → a) → Free f a → a liftF ∷ Functor f ⇒ f a → Free f a instance (Functor f) ⇒ Monad (Free f) where return = Pure Pure a ≫= f = f a Join mma ≫= f = Join (fmap (≫= f) mma)
  • 41. 自由モナド • 抽象構文木:モナド = 函手 + join + return • Free f はこれらを頂点に持つ木構造として定義出来る • Monad の定義は型をグッと睨めば導出できる data Free f α = Pure α | Join (f (Free f α)) deriving (Functor) handle' ∷ Functor f ⇒ (f a → a) → Free f a → a liftF ∷ Functor f ⇒ f a → Free f a instance (Functor f) ⇒ Monad (Free f) where return = Pure Pure a ≫= f = f a Join mma ≫= f = Join (fmap (≫= f) mma)
  • 42. Reader(環境モナド) • 函手にするため副作用を継続渡し形式 (CPS) で記述 • 複数の解釈が可能(自由モナドの普遍性) data Ask i a = Ask (i → a) deriving (Functor) ask ∷ Free (Ask e) e ask = liftF (Ask id) runReader ∷ e → Free (Ask e) a → a runReader e = handle (λ (Ask k) → k e) runIt ∷ [e] → Free (Ask e) a → a runIt _ (Pure a) = a runIt (x : xs) (Join (Ask k)) = runIt xs (k x)
  • 43. Reader(環境モナド) • 函手にするため副作用を継続渡し形式 (CPS) で記述 • 複数の解釈が可能(自由モナドの普遍性) data Ask i a = Ask (i → a) deriving (Functor) ask ∷ Free (Ask e) e ask = liftF (Ask id) runReader ∷ e → Free (Ask e) a → a runReader e = handle (λ (Ask k) → k e) runIt ∷ [e] → Free (Ask e) a → a runIt _ (Pure a) = a runIt (x : xs) (Join (Ask k)) = runIt xs (k x) 継続
  • 44. 複数の解釈が可能 ghci> runReader 12 $ ask >> (,) <$> ask <*> ask ⇒ (12,12) ghci> runIt [12, 23, 34] $ ask >> (,) <$> ask <*> ask ⇒ (23,34)
  • 46. 直和:State = Ask + Tell data Tell e a = Tell a e deriving (Functor) • Writer も同様に継続渡しで作れる
  • 47. 直和:State = Ask + Tell data Tell e a = Tell a e deriving (Functor) 継続 • Writer も同様に継続渡しで作れる
  • 48. 直和:State = Ask + Tell data Tell e a = Tell a e deriving (Functor) 継続 ログ出力 • Writer も同様に継続渡しで作れる
  • 49. 直和:State = Ask + Tell • Tell と Ask で State を創るには? data Tell e a = Tell a e deriving (Functor) 継続 ログ出力 • Writer も同様に継続渡しで作れる
  • 50. 直和:State = Ask + Tell • Tell と Ask で State を創るには? ➡ 函手の直和 f ⊕ g:f, g いずれかの構築子を持つ函手 data Tell e a = Tell a e deriving (Functor) 継続 ログ出力 • Writer も同様に継続渡しで作れる data (f ⊕ g) a = InL (f a) | InR (g a) deriving (Functor) runState ∷ s → Free (Tell s ⊕ Ask s) a → (a, s) runState _ (Join (InL (Tell fa e))) = runState e fa runState s0 (Join (InR (Ask k))) = runState s0 (k s0)
  • 51. 直和:State = Ask + Tell • Tell と Ask で State を創るには? ➡ 函手の直和 f ⊕ g:f, g いずれかの構築子を持つ函手 data Tell e a = Tell a e deriving (Functor) 継続 ログ出力 • Writer も同様に継続渡しで作れる data (f ⊕ g) a = InL (f a) | InR (g a) deriving (Functor) runState ∷ s → Free (Tell s ⊕ Ask s) a → (a, s) runState _ (Join (InL (Tell fa e))) = runState e fa runState s0 (Join (InR (Ask k))) = runState s0 (k s0) • 更に複数の効果を合成するには、多項和に一般化すればよい
  • 52. Open Union • Open Union: 函手の合成を任意個に一般化 data Union (fs ∷ [★ → ★]) a class f ∈ fs -- 「f は函手のリスト fs の要素」 inj ∷ f ∈ fs ⇒ f a → Union fs a -- InL, InR に対応 prj ∷ f ∈ fs ⇒ Union fs a → Maybe (f a) decomp ∷ Union (f : fs) a → Either (Union fs a) (f a) weaken ∷ Union fs a → Union (f : fs) a absurd ∷ Union '[] a → b • 旧 ExtEff [KSS13] では動的キャストを使用
  • 53. 完成:Extensible Effects • 原論文では、これに更にCPS変換を施している • モナド変換子の問題のうち、以下は解決 1. 意味論の固定 2. Too many lifts!!! 3. 合成順の固定 ๏ 3 については後ほど More の章で詳説 newtype Eff r a = Eff { unEff ∷ Free (Union r) a } deriving (Functor, Monad, Applicative)
  • 55. Extensible Effects まとめ Extensible Effects ‖ Free Monad + Open Union + 継続渡し形式(CPS)
  • 57. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト
  • 58. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き
  • 59. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい
  • 60. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい • ライブラリ開発が面倒
  • 61. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい • ライブラリ開発が面倒 • 技術的な問題:動的キャストの利用
  • 62. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい • ライブラリ開発が面倒 • 技術的な問題:動的キャストの利用 • 実行時コストが嵩む
  • 63. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい • ライブラリ開発が面倒 • 技術的な問題:動的キャストの利用 • 実行時コストが嵩む • 基底モナドに ST s などSkolem変数を含む型が使えない
  • 64. Ext Eff の問題点 • 合成に Functor が必要、fmap にコスト • 自由モナドは効率が悪く、reflectionにも不向き • 継続ベースで、ハンドラが書きづらい • ライブラリ開発が面倒 • 技術的な問題:動的キャストの利用 • 実行時コストが嵩む • 基底モナドに ST s などSkolem変数を含む型が使えない ★ More Extensible Effects (提案手法)はこれらを解決
  • 65. Freer Monads, 
 More Extensible Effects
  • 66. Extensible Effects Extensible Effects ‖ Free Monad + Open Union + 継続渡し形式(CPS)
  • 67. More
 Extensible Effects More Extensible Effects ‖ Freer Monad + Open Union + Type-aligned Sequence = Operational Monad
  • 69. Freer Monad とは • ??「Freer Monad は単項型の恒等函手に沿っ
    た左 Kan 拡張だよ。何か問題でも?」
  • 70. Freer Monad とは • ??「Freer Monad は単項型の恒等函手に沿っ
    た左 Kan 拡張だよ。何か問題でも?」 ➡ 問題しかない(少なくとも私にとっては)
  • 71. Freer Monad とは • ??「Freer Monad は単項型の恒等函手に沿っ
    た左 Kan 拡張だよ。何か問題でも?」 ➡ 問題しかない(少なくとも私にとっては) • これまでを踏まえた説明:
  • 72. Freer Monad とは • ??「Freer Monad は単項型の恒等函手に沿っ
    た左 Kan 拡張だよ。何か問題でも?」 ➡ 問題しかない(少なくとも私にとっては) • これまでを踏まえた説明: • Freer Monad = 自由 Functor + 自由モナド
  • 73. 自由 Functor と
 Freer Monad data FFree f a = ∀ b. FMap (b → a) (f b) instance Functor (FFree f) where fmap f (FMap g a) = FMap (f ◦ g) a type Freer f a = Free (FFree f) a
  • 74. 自由 Functor と
 Freer Monad data FFree f a = ∀ b. FMap (b → a) (f b) instance Functor (FFree f) where fmap f (FMap g a) = FMap (f ◦ g) a type Freer f a = Free (FFree f) a fmap !
  • 75. 自由 Functor と
 Freer Monad • 任意の単項型構築子 f ∷ ★ → ★ に対し、FFree f ∷ ★ → ★ は自動的にFunctor (= 自由Functor = Idに沿った左Kan拡張) data FFree f a = ∀ b. FMap (b → a) (f b) instance Functor (FFree f) where fmap f (FMap g a) = FMap (f ◦ g) a type Freer f a = Free (FFree f) a fmap !
  • 76. 自由 Functor と
 Freer Monad • 任意の単項型構築子 f ∷ ★ → ★ に対し、FFree f ∷ ★ → ★ は自動的にFunctor (= 自由Functor = Idに沿った左Kan拡張) data FFree f a = ∀ b. FMap (b → a) (f b) instance Functor (FFree f) where fmap f (FMap g a) = FMap (f ◦ g) a type Freer f a = Free (FFree f) a ➡ FFree と Free で Functorを仮定せずにモナドが出来る
  • 78. Freer の定義の導出 • Free と FFree の定義を展開する Freer f a = Free (FFree f) a ≃ Pure a | Impure (FFree f (Freer f a)) ≃ Pure a | ∀ b. Impure (f b) (b → Freer f a)
  • 79. Freer の定義の導出 • Free と FFree の定義を展開する Freer f a = Free (FFree f) a ≃ Pure a | Impure (FFree f (Freer f a)) ≃ Pure a | ∀ b. Impure (f b) (b → Freer f a) • f を OpenUnionで置き換えれば: data Eff r a = Pure a | ∀ b. Impure (Union r b) (b → Eff r a)
  • 80. Freer の定義の導出 • Free と FFree の定義を展開する Freer f a = Free (FFree f) a ≃ Pure a | Impure (FFree f (Freer f a)) ≃ Pure a | ∀ b. Impure (f b) (b → Freer f a) • f を OpenUnionで置き換えれば: data Eff r a = Pure a | ∀ b. Impure (Union r b) (b → Eff r a) return
  • 81. Freer の定義の導出 • Free と FFree の定義を展開する Freer f a = Free (FFree f) a ≃ Pure a | Impure (FFree f (Freer f a)) ≃ Pure a | ∀ b. Impure (f b) (b → Freer f a) • f を OpenUnionで置き換えれば: data Eff r a = Pure a | ∀ b. Impure (Union r b) (b → Eff r a) return bind (>>=)!
  • 82. インスタンス定義 • 継続を合成していけば良いだけの素直な実装 • Functor 不要!(fmap f a = (return . f) =<< a) data Eff r a = Pure a | ∀ b. Impure (Union r b) (b → Eff r a) instance Monad (Eff f) where return = Pure (Pure a) ≫= f = f a (Impure fa k) ≫= f = Impure fa (k >=> f)
  • 83. ハンドラ実装用便利関数 • 古い ExtEff だとここまで使い易い型にならない • リレーしていくだけなのでこれらの実装も容易 • ループ内部状態ありの版や、効果を取り除かない版も可能 handle_relay ∷ (a → Eff r w) → (∀ v. t v → (v → Eff r w) → Eff r w) → Eff (t ': r) a → Eff r w handle_relay ret h m = ... send ∷ (t ∈ r) ⇒ t v → Eff r v send cmd = Impure (inj cmd) Pure return ! (>>=) !
  • 84. 例:Ask, Tell 再訪 data Ask i a where Ask ∷ Ask i i data Tell i a where Tell ∷ i → Tell i () ask ∷ (Ask i ∈ r) ⇒ Eff r i ask = send Ask tell ∷ (Tell i ∈ r) ⇒ i → Eff r () tell = send ◦ Tell
  • 85. 例:Ask, Tell 再訪 • プリミティヴな関数とほぼ同じ型のデータ構築子を用意 data Ask i a where Ask ∷ Ask i i data Tell i a where Tell ∷ i → Tell i () ask ∷ (Ask i ∈ r) ⇒ Eff r i ask = send Ask tell ∷ (Tell i ∈ r) ⇒ i → Eff r () tell = send ◦ Tell
  • 86. 例:Ask, Tell 再訪 • プリミティヴな関数とほぼ同じ型のデータ構築子を用意 ➡ 旧来の ExtEff での定義に比べて非常に素直 data Ask i a where Ask ∷ Ask i i data Tell i a where Tell ∷ i → Tell i () ask ∷ (Ask i ∈ r) ⇒ Eff r i ask = send Ask tell ∷ (Tell i ∈ r) ⇒ i → Eff r () tell = send ◦ Tell
  • 87. ハンドラの実装 • 実質 return と (>>=) を書くだけなので楽 runReader ∷ e → Eff (Ask e : r) a → Eff r a runReader e = handle_relay return (λ Ask arr → arr e) runIt :: [e] → Eff (Ask e : r) a → Eff r a runIt = handle_relay_s (λ _ a → return a) $ λ (x : xs) Ask arr → arr xs x runWriter ∷ Eff (Tell e : r) a → Eff r (a, [e]) runWriter = handle_relay (λa → return (a, [])) $ λ (Tell e) f → do { (a, es) ← f () ; return (a, e:es) }
  • 88. More
 Extensible Effects More Extensible Effects ‖ Freer Monad + Open Union + Type-aligned Sequence = Operational Monad
  • 89. More
 Extensible Effects More Extensible Effects ‖ Freer Monad + Open Union + Type-aligned Sequence
  • 90. これまでの実装の欠点 • Eff モナドインスタンスの定義 instance Monad (Eff f) where return = Pure (Pure x) ≫= f = f x (Impure fa k) ≫= f = Impure fa (k >=> f)
  • 91. これまでの実装の欠点 • Eff モナドインスタンスの定義 instance Monad (Eff f) where return = Pure (Pure x) ≫= f = f x (Impure fa k) ≫= f = Impure fa (k >=> f) • 継続が第二引数に左結合的に蓄積されていく
  • 92. これまでの実装の欠点 • Eff モナドインスタンスの定義 instance Monad (Eff f) where return = Pure (Pure x) ≫= f = f x (Impure fa k) ≫= f = Impure fa (k >=> f) • 継続が第二引数に左結合的に蓄積されていく • ハンドラは命令列を頭から逐次実行する • 左結合だと、最初の数個だけが必要でも継続全体を走査する 必要がある!
  • 93. これまでの実装の欠点 • Eff モナドインスタンスの定義 instance Monad (Eff f) where return = Pure (Pure x) ≫= f = f x (Impure fa k) ≫= f = Impure fa (k >=> f) • 継続が第二引数に左結合的に蓄積されていく • ハンドラは命令列を頭から逐次実行する • 左結合だと、最初の数個だけが必要でも継続全体を走査する 必要がある! ➡ 解決策:Type-aligned Sequence [PK14]
  • 97. Type-aligned Sequence • 直感:関数を実際に合成する代わりに関数の列を 考えて必要になったら適宜評価していけばよい • 安価な snoc, cat, uncons を持つ二分木 + 型 Hack • 初出では Reflection w/o Remorse そのものを使っていた
  • 98. Type-aligned Sequence • 直感:関数を実際に合成する代わりに関数の列を 考えて必要になったら適宜評価していけばよい • 安価な snoc, cat, uncons を持つ二分木 + 型 Hack • 初出では Reflection w/o Remorse そのものを使っていた type FTCQ (m ∷ ★ → ★) a b tsingleton ∷ (a → m b) → FTCQ m a b (▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b (⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b data ViewL m a b where TOne ∷ (a → m b) → ViewL m a b (:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b tviewl ∷ FTCQ m a b → ViewL m a b
  • 99. Type-aligned Sequence • 直感:関数を実際に合成する代わりに関数の列を 考えて必要になったら適宜評価していけばよい • 安価な snoc, cat, uncons を持つ二分木 + 型 Hack • 初出では Reflection w/o Remorse そのものを使っていた type FTCQ (m ∷ ★ → ★) a b tsingleton ∷ (a → m b) → FTCQ m a b (▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b (⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b data ViewL m a b where TOne ∷ (a → m b) → ViewL m a b (:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b tviewl ∷ FTCQ m a b → ViewL m a b a → m b に対応
  • 100. Type-aligned Sequence • 直感:関数を実際に合成する代わりに関数の列を 考えて必要になったら適宜評価していけばよい • 安価な snoc, cat, uncons を持つ二分木 + 型 Hack • 初出では Reflection w/o Remorse そのものを使っていた type FTCQ (m ∷ ★ → ★) a b tsingleton ∷ (a → m b) → FTCQ m a b (▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b (⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b data ViewL m a b where TOne ∷ (a → m b) → ViewL m a b (:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b tviewl ∷ FTCQ m a b → ViewL m a b a → m b に対応 (>=>)
  • 101. Type-aligned Sequence • 直感:関数を実際に合成する代わりに関数の列を 考えて必要になったら適宜評価していけばよい • 安価な snoc, cat, uncons を持つ二分木 + 型 Hack • 初出では Reflection w/o Remorse そのものを使っていた type FTCQ (m ∷ ★ → ★) a b tsingleton ∷ (a → m b) → FTCQ m a b (▹) ∷ FTCQ m a c → (c → m b) → FTCQ m a b (⋈) ∷ FTCQ m a c → FTCQ m c b → FTCQ m a b data ViewL m a b where TOne ∷ (a → m b) → ViewL m a b (:|) ∷ (a → m c) → (FTCQ m c b) → ViewL m a b tviewl ∷ FTCQ m a b → ViewL m a b a → m b に対応 (>=>) uncons
  • 102. 最終結果 • Union と FTCQ の実装を改善していけば、効率 が劇的に改善する type Arrs r a b = FTCQ (Eff r) a b data Eff r a = Pure a | ∀ b. Impure (Union r b) (Arrs r b a) instance Monad (Eff r) where return = Pure (Pure a) ≫= f = f a (Impure fa k) ≫= f = Impure fa (k ▹ f) handle_relay ∷ ...
  • 105. Example : 例外処理 • MTL が最も苦手とする物の一つ • 「階層を跨いだ処理」が効いてくる場面の一つ • (以下、わかりやすさのため Refl w/o Remorse 適用前のコード)
  • 106. 例外作用の定義 data Exc e a = ThrowError e throwError ∷ Exc e ∈ r ⇒ e → Eff r a catchError ∷ ∀ e r a. Exc e ∈ r
 ⇒ Eff r a → (e → Eff r a) → Eff r a catchError act h = interpose return bind act where bind ∷ Exc e b → (b → Eff r a) → Eff r a bind (Exc e) _ = h e runError ∷ Eff (Exc e : r) a → Eff r (Either e a) runError = handle_relay (return ◦ Right) $ λ(ThrowError e) _ → return (Left e)
  • 107. 例外作用の定義 data Exc e a = ThrowError e throwError ∷ Exc e ∈ r ⇒ e → Eff r a catchError ∷ ∀ e r a. Exc e ∈ r
 ⇒ Eff r a → (e → Eff r a) → Eff r a catchError act h = interpose return bind act where bind ∷ Exc e b → (b → Eff r a) → Eff r a bind (Exc e) _ = h e runError ∷ Eff (Exc e : r) a → Eff r (Either e a) runError = handle_relay (return ◦ Right) $ λ(ThrowError e) _ → return (Left e) • interpose: 作用を除去しないハンドラを書くためのもの
  • 108. 例外作用の定義 data Exc e a = ThrowError e throwError ∷ Exc e ∈ r ⇒ e → Eff r a catchError ∷ ∀ e r a. Exc e ∈ r
 ⇒ Eff r a → (e → Eff r a) → Eff r a catchError act h = interpose return bind act where bind ∷ Exc e b → (b → Eff r a) → Eff r a bind (Exc e) _ = h e runError ∷ Eff (Exc e : r) a → Eff r (Either e a) runError = handle_relay (return ◦ Right) $ λ(ThrowError e) _ → return (Left e) • interpose: 作用を除去しないハンドラを書くためのもの • 例外があれば対処すれば良いだけなので素直で簡潔
  • 109. トランザクション • 例外等で動作が中断した場合、継続は呼ばれない ➡ Pure で末端に達したら状態を大域的に反映すればよい transaction ∷ ∀ s r a. (State s) ∈ r ⇒ Proxy s → Eff r a → Eff r a transaction _ act = do s ← get; loop s act where loop ∷ s → Eff r a → Eff r a loop s (Pure a) = put s ≫ return a loop s (Impure (u ∷ Union r b) k) = case prj u ∷ Maybe (State s b) of Just Get → loop s $ k s Just (Put s') → loop s' $ k () Nothing → Impure u (loop s ◦ k)
  • 110. 実行例 transTest = runLift $ flip runState True $ flip runState 'a' $ runError' (Proxy ∷ Proxy String) $ handled $ transaction (Proxy ∷ Proxy Bool) $ do modify (succ ∷ Char → Char) modify not throwError "interrupted!" where handled = flip catchError $ λ str → lift (putStrLn ("ignored: " ++ str)) ghci> transTest ignored: interrupted! ==> ((Right (), 'b'), True)
  • 111. 実行例 transTest = runLift $ flip runState True $ flip runState 'a' $ runError' (Proxy ∷ Proxy String) $ handled $ transaction (Proxy ∷ Proxy Bool) $ do modify (succ ∷ Char → Char) modify not throwError "interrupted!" where handled = flip catchError $ λ str → lift (putStrLn ("ignored: " ++ str)) ghci> transTest ignored: interrupted! ==> ((Right (), 'b'), True) Bool の状態のみ
 トランザクションで保護
  • 112. 実行例 transTest = runLift $ flip runState True $ flip runState 'a' $ runError' (Proxy ∷ Proxy String) $ handled $ transaction (Proxy ∷ Proxy Bool) $ do modify (succ ∷ Char → Char) modify not throwError "interrupted!" where handled = flip catchError $ λ str → lift (putStrLn ("ignored: " ++ str)) ghci> transTest ignored: interrupted! ==> ((Right (), 'b'), True) 保護されていたBool
 の変更は反映されない Bool の状態のみ
 トランザクションで保護
  • 113. MTLでは? • 階層を跨げないので transaction に相当する関数は実装不能 • しかも合成順によって挙動が違う! • ExceptT が先頭なら全部コミット、そうでなきゃロールバック ghci> let handler = flip catchError $ λs → liftIO (putStrLn ("ignored: " ++ s)) ghci> runExceptT $ flip runStateT True $ handler $ (modify not ≫ throwError "hay") ignored: hay ==> Right ((),True) ghci> flip runStateT True $ runExceptT $ handler $ (modify not ≫ throwError "hay") ignored: hay ==> (Right (),False)
  • 114. まとめ・その他 • (More) Extensible Effects では、階層を跨いだ ハンドラが実装出来る • MTL では実現不可能だった処理も実装可能 • I/Oエラーの捕捉や、Error モナドへのリレーも容易 • 原論文 [KI15] には論理モナドやリージョン計算 など、他の例もあり • More EE それ自体をモナド変換子として用いる 事も可能(基底モナド付き計算)
  • 116. Benchmarks • メイン:5の倍数を数える • その上下に余分な Reader 層 を足し計算を実行 • MTL、旧ExtEff、MoreEE と 競合手法の HIA [KSS13] を 比較 • 環境:Intel Core i7 2.8GHz, 16GB RAM, GHC 7.10.3.
 -threaded -O2 −rtsopts benchS ns = foldM f 1 ns where f acc x | x `mod` 5 == 0 = do s ← S.get S.put $! (s+1) return $! max acc x f acc x = return $! max acc x {-# NOINLINE benchS #-} NOINLINE を付けて大きな計算と見做す (GHC 7.8 までは付けなくてもMTLが 圧倒的に悪かった)
  • 117. Readers under State 実行時間(sec) 0 1.25 2.5 3.75 5 0 1 2 3 4 MTL HIA Old ExtEff More ExtEff TotalAlloc(KBytes) 0 200 400 600 800 0 250 500 750 1000 Reader の数
  • 118. TotalAlloc(Kbytes) 0 200 400 600 800 0 250 500 750 1000 Reader の数 Readers over State GHC 7.8 ではより O(n2) に近い勾配 実行時間(sec) 0 2.25 4.5 6.75 9 0 2 4 6 8 MTL HIA Old ExtEff More ExtEff
  • 119. 補足と考察 • 旧 Ext Eff から大幅に改善 • Reader層を State 層に置き換えると、両方の場 合についてNOINLINE 付きでも MTL が線型速度 • 大きな計算の場合、時間・空間消費量ともに More Ext Eff が最適、競合手法とも互角 • INLINE が効く小さい計算、あるいは単純な State / Reader 計算は最適化が効いて MTL が勝つ • GHC の最適化機構の進化は凄まじい
  • 121. Conclusions • More Extensible Effects は MTL の代替 ๏ 高効率 • 時間・空間計算量が、競合手法の中でもかなり効率的 • 多数の副作用を合成する際に威力を発揮 ๏ 柔軟で高い表現力 • 制約無しに副作用を合成可能 • 階層を跨いだ処理、複数の解釈を許容 ๏ No More Lifts!
  • 122. 参考文献 [KI15] Kiselyov and ISHII, Freer Monads, More Extensible Effects. Haskell '15. [KLO13] Kammar, S. Lindley, and N. Oury, Handlers in action. ICFP '13. [KSS13] Kiselyov, Sabry and Swords, Extensible Effects: An Alternative to
 Monad Transformers. Haskell '13. [PK14] van der Ploeg and Kiselyov, Reflection without Remorse: Revealing a
 hidden sequence to speed up monadic reflection. Haskell '13.
  • 124. Any Questions? • More Extensible Effects は MTL の代替 ๏ 高効率 • 時間・空間計算量が、競合手法の中でもかなり効率的 • 多数の副作用を合成する際に威力を発揮 ๏ 柔軟で高い表現力 • 制約無しに副作用を合成可能 • 階層を跨いだ処理、複数の解釈を許容 ๏ No More Lifts!