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

「型の理論」と証明支援システム -- COQの世界
「型の理論」と証明支援システム -- COQの世界「型の理論」と証明支援システム -- COQの世界
「型の理論」と証明支援システム -- COQの世界maruyama097
 
Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)Masahiro Sakai
 
ラムダ計算入門
ラムダ計算入門ラムダ計算入門
ラムダ計算入門Eita Sugimoto
 
関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCamlHaruka Oikawa
 
Extensible Eff Applicative
Extensible Eff ApplicativeExtensible Eff Applicative
Extensible Eff ApplicativeSanshiro Yoshida
 
技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底Hiromi Ishii
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015CODE BLUE
 
型安全性入門
型安全性入門型安全性入門
型安全性入門Akinori Abe
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
自動定理証明の紹介
自動定理証明の紹介自動定理証明の紹介
自動定理証明の紹介Masahiro Sakai
 
証明プログラミング超入門
証明プログラミング超入門証明プログラミング超入門
証明プログラミング超入門Kyoko Kadowaki
 
OWLで何が書けるか
OWLで何が書けるかOWLで何が書けるか
OWLで何が書けるかKazuro Fukuhara
 
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みSAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みMasahiro Sakai
 
よくわかるCoqプログラミング
よくわかるCoqプログラミングよくわかるCoqプログラミング
よくわかるCoqプログラミングReal_analysis
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!Ra Zon
 
暗号文のままで計算しよう - 準同型暗号入門 -
暗号文のままで計算しよう - 準同型暗号入門 -暗号文のままで計算しよう - 準同型暗号入門 -
暗号文のままで計算しよう - 準同型暗号入門 -MITSUNARI Shigeo
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 
ELFの動的リンク
ELFの動的リンクELFの動的リンク
ELFの動的リンク7shi
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenMITSUNARI Shigeo
 
数式を綺麗にプログラミングするコツ #spro2013
数式を綺麗にプログラミングするコツ #spro2013数式を綺麗にプログラミングするコツ #spro2013
数式を綺麗にプログラミングするコツ #spro2013Shuyo Nakatani
 

What's hot (20)

「型の理論」と証明支援システム -- COQの世界
「型の理論」と証明支援システム -- COQの世界「型の理論」と証明支援システム -- COQの世界
「型の理論」と証明支援システム -- COQの世界
 
Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)
 
ラムダ計算入門
ラムダ計算入門ラムダ計算入門
ラムダ計算入門
 
関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml
 
Extensible Eff Applicative
Extensible Eff ApplicativeExtensible Eff Applicative
Extensible Eff Applicative
 
技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底技術者が知るべき Gröbner 基底
技術者が知るべき Gröbner 基底
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
 
型安全性入門
型安全性入門型安全性入門
型安全性入門
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
自動定理証明の紹介
自動定理証明の紹介自動定理証明の紹介
自動定理証明の紹介
 
証明プログラミング超入門
証明プログラミング超入門証明プログラミング超入門
証明プログラミング超入門
 
OWLで何が書けるか
OWLで何が書けるかOWLで何が書けるか
OWLで何が書けるか
 
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組みSAT/SMTソルバの仕組み
SAT/SMTソルバの仕組み
 
よくわかるCoqプログラミング
よくわかるCoqプログラミングよくわかるCoqプログラミング
よくわかるCoqプログラミング
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!
 
暗号文のままで計算しよう - 準同型暗号入門 -
暗号文のままで計算しよう - 準同型暗号入門 -暗号文のままで計算しよう - 準同型暗号入門 -
暗号文のままで計算しよう - 準同型暗号入門 -
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
ELFの動的リンク
ELFの動的リンクELFの動的リンク
ELFの動的リンク
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
 
数式を綺麗にプログラミングするコツ #spro2013
数式を綺麗にプログラミングするコツ #spro2013数式を綺麗にプログラミングするコツ #spro2013
数式を綺麗にプログラミングするコツ #spro2013
 

Similar to Freer Monads, More Extensible Effects

すごい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
 
すごい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

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
 
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 (14)

Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
Lebesgue可測性に関するSoloayの定理と実数の集合の正則性Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
Lebesgue可測性に関するSoloayの定理と実数の集合の正則性
 
実数の集合はどこまで可測になれるか?
実数の集合はどこまで可測になれるか?実数の集合はどこまで可測になれるか?
実数の集合はどこまで可測になれるか?
 
(数式の入った)本をつくる
(数式の入った)本をつくる(数式の入った)本をつくる
(数式の入った)本をつくる
 
御清聴ありがとうございました
御清聴ありがとうございました御清聴ありがとうございました
御清聴ありがとうございました
 
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
Lebesgue 可測性に関する Solovay-Shelah の結果に必要な記述集合論のごく基本的な事項
 
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 とか
 
実践・完全犯罪のつくり方
実践・完全犯罪のつくり方実践・完全犯罪のつくり方
実践・完全犯罪のつくり方
 

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!