継続モナド
Contモナド
型の定義
code:hs
newtype Cont r a = Cont {
runCont :: (a -> r) -> r
}
型引数の意味を理解する
例えば、普通に継続渡しでaddを書いてみる
code:hs
addCPS :: Num a => a -> a -> (a -> r) -> r
addCPS x y k = k $ x + y
これと上の(a -> r) -> rの対応を考えると、以下のようになっていることがわかる
aは継続渡し以前のadd関数の計算結果の型
a -> rは、継続kの型
rは、継続kに「addの計算結果」を適用した型
実装
code:hs
instance Monad (Cont r) where
return a = Cont $ \k -> k a
(Cont c) >>= f = Cont $ \k -> c (\a -> runCont (f a) k)
継続を使う前後の比較 ref
https://gyazo.com/7c06ed70ca2922751a0fc4bbe1d999af
上が継続モナドなし、下があり
黄色の部分にとって、緑の部分が継続になっている
↑の図には色を付けていないが内側も同様
継続の入れ子になっている
定義の意味
Contモナドを実装する - TOKYO OYASUMI CLUB
https://jutememo.blogspot.com/2012/05/haskell.html
https://r-west.hatenablog.com/entry/20070520/1179686305
http://blog.sigfpe.com/2008/12/mother-of-all-monads.html
CPSを直接スタイルで書くことができる
Haskellでは遅延評価があるため継続はあまり必要にならない ref
そうなん?mrsekut.icon
嬉しさ
CPSを直接スタイルで書くことができる
結果的にコールバック地獄が消え、簡潔になる
途中の?継続を扱いやすくなる
これはよくわかっていないmrsekut.icon
たぶん普通に継続の嬉しさの話なんだろうけど、それをそもそも知らない
call/ccができる
pursでは、最初からContTで作られ、ContはそのIdentityで定義している
https://pursuit.purescript.org/packages/purescript-transformers/5.1.0/docs/Control.Monad.Cont.Trans
https://pursuit.purescript.org/packages/purescript-transformers/5.1.0/docs/Control.Monad.Cont
素直に実装すると以下のようになる
code:purs(hs)
newtype Cont r a = Cont ((a -> r) -> r)
instance Functor (Cont r) where
map f (Cont m) = Cont (\k -> m (\a -> k $ f a))
instance Apply (Cont r) where
apply (Cont f) (Cont v) = Cont (\k -> f (\g -> v (\a -> k (g a))))
instance Applicative (Cont r) where
pure a = Cont (\k -> k a)
instance Bind (Cont r) where
bind (Cont m) k = Cont (\k' -> m (\a -> case k a of Cont m' -> m' k'))
instance Monad (Cont r)
class Monad m <= MonadCont m where
callCC :: forall a. ((forall b. a -> m b) -> m a) -> m a
instance MonadCont (Cont r) where
callCC f = Cont (\k -> case f (\a -> Cont (\_ -> k a)) of Cont f' -> f' k)
https://zenn.dev/eagle/articles/double-negation-monad
二重否定モナド
MonadCont型クラス
ref http://www.sampou.org/haskell/a-a-monads/html/contmonad.html
callCCを提供する
code:hs
class (Monad m) => MonadCont m where
callCC :: ((a -> m b) -> m a) -> m a
instance MonadCont (Cont r) where
callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k
kは、fの継続
https://wiki.haskell.org/MonadCont_done_right
応用例
大域脱出
https://qiita.com/tanakh/items/55441a27f1df878b853e
https://qiita.com/lotz/items/a1ff5725e918e216940e
リソース管理
https://qiita.com/tanakh/items/81fc1a0d9ae0af3865cb
直線作図機能
https://u1roh.hatenadiary.jp/entry/2014/12/18/000849
https://qiita.com/pab_tech/items/fc3d160a96cecdead622