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

Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Preferred Networks
 
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みSAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みMasahiro Sakai
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話Nagisa Eto
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数Shinichi Kozake
 
数学プログラムを Haskell で書くべき 6 の理由
数学プログラムを Haskell で書くべき 6 の理由数学プログラムを Haskell で書くべき 6 の理由
数学プログラムを Haskell で書くべき 6 の理由Hiromi Ishii
 
Scala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたScala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたKazuyuki TAKASE
 
すごい配列楽しく学ぼう
すごい配列楽しく学ぼうすごい配列楽しく学ぼう
すごい配列楽しく学ぼうxenophobia__
 
よくわかるCoqプログラミング
よくわかるCoqプログラミングよくわかるCoqプログラミング
よくわかるCoqプログラミングReal_analysis
 
OSS強化学習フレームワークの比較
OSS強化学習フレームワークの比較OSS強化学習フレームワークの比較
OSS強化学習フレームワークの比較gree_tech
 
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~Takuya Akiba
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたってTsuyoshi Matsudate
 
Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Kenjiro Kubota
 
TalkingData AdTracking Fraud Detection Challenge (1st place solution)
TalkingData AdTracking  Fraud Detection Challenge (1st place solution)TalkingData AdTracking  Fraud Detection Challenge (1st place solution)
TalkingData AdTracking Fraud Detection Challenge (1st place solution)Takanori Hayashi
 
今さら聞けないカーネル法とサポートベクターマシン
今さら聞けないカーネル法とサポートベクターマシン今さら聞けないカーネル法とサポートベクターマシン
今さら聞けないカーネル法とサポートベクターマシンShinya Shimizu
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!Ra Zon
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界Preferred Networks
 

What's hot (20)

Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
 
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みSAT/SMTソルバの仕組み
SAT/SMTソルバの仕組み
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数
 
数学プログラムを Haskell で書くべき 6 の理由
数学プログラムを Haskell で書くべき 6 の理由数学プログラムを Haskell で書くべき 6 の理由
数学プログラムを Haskell で書くべき 6 の理由
 
Scala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたScala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみた
 
すごい配列楽しく学ぼう
すごい配列楽しく学ぼうすごい配列楽しく学ぼう
すごい配列楽しく学ぼう
 
Marp入門
Marp入門Marp入門
Marp入門
 
よくわかるCoqプログラミング
よくわかるCoqプログラミングよくわかるCoqプログラミング
よくわかるCoqプログラミング
 
直交領域探索
直交領域探索直交領域探索
直交領域探索
 
OSS強化学習フレームワークの比較
OSS強化学習フレームワークの比較OSS強化学習フレームワークの比較
OSS強化学習フレームワークの比較
 
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたって
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。
 
TalkingData AdTracking Fraud Detection Challenge (1st place solution)
TalkingData AdTracking  Fraud Detection Challenge (1st place solution)TalkingData AdTracking  Fraud Detection Challenge (1st place solution)
TalkingData AdTracking Fraud Detection Challenge (1st place solution)
 
今さら聞けないカーネル法とサポートベクターマシン
今さら聞けないカーネル法とサポートベクターマシン今さら聞けないカーネル法とサポートベクターマシン
今さら聞けないカーネル法とサポートベクターマシン
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界
 

Similar to Freer Monads, More Extensible Effects

Extensible Eff Applicative
Extensible Eff ApplicativeExtensible Eff Applicative
Extensible Eff ApplicativeSanshiro Yoshida
 
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(後編)Nozomu Kaneko
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたってTsuyoshi Matsudate
 
すごいHaskell読書会#10
すごいHaskell読書会#10すごいHaskell読書会#10
すごいHaskell読書会#10Shin Ise
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座bleis tift
 
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
 
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
 
モナドがいっぱい!
モナドがいっぱい!モナドがいっぱい!
モナドがいっぱい!Kenta Sato
 
マスターオブゴールーチンアンドチャネル スタートGo #1
マスターオブゴールーチンアンドチャネル   スタートGo #1マスターオブゴールーチンアンドチャネル   スタートGo #1
マスターオブゴールーチンアンドチャネル スタートGo #1Takuya Ueda
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーmganeko
 
Chainerの使い方と 自然言語処理への応用
Chainerの使い方と自然言語処理への応用Chainerの使い方と自然言語処理への応用
Chainerの使い方と 自然言語処理への応用Yuya Unno
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料時響 逢坂
 

Similar to Freer Monads, More Extensible Effects (16)

Haskell Lecture 2
Haskell Lecture 2Haskell Lecture 2
Haskell Lecture 2
 
Extensible Eff Applicative
Extensible Eff ApplicativeExtensible Eff Applicative
Extensible Eff Applicative
 
すごい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読書会#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

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

新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdffurutsuka
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 

Recently uploaded (9)

新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdf
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 

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!