Freeモナド
Monad型クラス
自由モナド
任意のFunctor型クラスをMonadにすることができる
Free型
HaskellでのFreeモナドの定義 ref
Freeの構成を、PureとJoinでした場合の定義
code:hs
data Free f a = Pure a
| Join (f (Free f a))
instance Functor f => Functor (Free f) where
fmap = (<$>)
-- fmap f (Pure x) = Pure $ f x
-- fmap f (Join fx) = Join $ fmap (fmap f) fx
instance (Functor f) => Applicative (Free f) where
pure = Pure
Pure f <*> a = fmap f a
Join f <*> a = Join $ fmap (<*> a) f
instance Functor f => Monad (Free f) where
return = Pure
Pure x >>= f = f x
Join x >>= f = Join $ fmap (>>= f) x
これが最も一般的mrsekut.icon
FreeをPureとBindで構成した場合
code:hs
data Free f a where
Pure :: a -> Free f a
Bind :: f x -> (x -> Free f a) -> Free f a
instance Functor (Free f) where
fmap = (<$>)
instance Applicative (Free f) where
pure = Pure
f <*> x = f >>= \f' -> x >>= \x' -> pure $ f' x'
instance Monad (Free f) where
return = Pure
(>>=) (Pure a) a2fb = a2fb a
(>>=) (Bind fx x2fa) a2fb = Bind fx (a2fb <=< x2fa)
ref Free型はMonadをdata構造として表現したもの
Monadとなる条件を考えると、PureとBindで十分なので、「fがFunctorである」という条件は不要になる
参考
Haskell for all: Why free monads matter
不動点の話からFreeの話へ繋げる
自動的にAlgebraic Effects and Handlersなどへの伏線となってる感があるmrsekut.icon
Freeモナドって何なのさっ!? - capriccioso String Creating(Object something){ return My.Expression(something); }
わかりやすいmrsekut.icon
説明がそれなりに簡潔で良い
出てくるコードは上のHaskell for allの類似
Freeモナドのしくみ - Qiita
かなり親切で詳細
これ読んだおかげで細かい箇所の解像度が上がったmrsekut.icon
free monadとはmonadそのものである - うさぎ小屋
Freeモナドの4種の定義
#WIP
ArchitectureとしてのFreeモナド
lotz (@lotz84_)
"String Diagrams for Free Monads"
Freeモナドの持つ外延的な性質(emb, interp)をストリング図に落とし込むことでFreeモナドやFreeモナドトランスフォーマーの満たす性質をストリング図で証明する話
https://research-information.bris.ac.uk/ws/portalfiles/portal/87127912/Nicolas_Wu_String_Diagrams_for_Free_Monads.pdf
GPT-4o.icon
🧠 3. 直感的な理解
Free f a は「f による命令を並べたリストのようなもの」と思ってください。
Pure a は「もうこれ以上命令はない(終了)」。
Free (f (Free f a)) は「ひとつ命令を実行し、次の命令を続ける」。
🧪 4. 例:簡単なDSLをFreeモナドで作る
たとえば、「コンソール入出力」を表すDSLを考えましょう。
code:haskell
data ConsoleF next
= ReadLine (String -> next)
| WriteLine String next
deriving Functor
これを Free で包むと、モナドになります:
code:haskell
type Console = Free ConsoleF
命令を組み立てる:
code:haskell
readLine :: Console String
readLine = liftF (ReadLine id)
writeLine :: String -> Console ()
writeLine s = liftF (WriteLine s ())
使ってみる:
code:haskell
program :: Console ()
program = do
name <- readLine
writeLine ("Hello, " ++ name)
ここでは「何も実行していない」。
ただ、命令の木構造(抽象構文木)を作っているだけです。
🏃 5. 実行(interpretation)
このDSLを「どう実行するか」は別途定義します。
code:haskell
runConsoleIO :: Console a -> IO a
runConsoleIO (Pure a) = pure a
runConsoleIO (Free (ReadLine next)) = getLine >>= runConsoleIO . next
runConsoleIO (Free (WriteLine s next)) = putStrLn s >> runConsoleIO next
また、IO以外にも「テスト用モック」など別の解釈を用意できます。
🪄 6. liftF と Freeモナドの由来
liftF は次のように定義されます:
code:haskell
liftF :: Functor f => f a -> Free f a
liftF fa = Free (fmap Pure fa)
これは「単一の命令をFreeモナドに持ち上げる」関数。
Freeの由来は「最も自由なモナド」、つまり追加の制約を一切加えず、最小限の構造でモナド化したものという意味です。
圏論
https://qiita.com/karrym/items/41cd9facae3a04d97703
https://hiratara.hatenadiary.jp/entry/20121011/1349921356
https://ncatlab.org/nlab/show/free+monad
https://halcat.org/scala/freeap/index.html
https://zenn.dev/funnycat/articles/a8eb151c2f9a35
pursの場合
https://pursuit.purescript.org/packages/purescript-free/6.0.1/docs/Control.Monad.Free
むかし
https://github.com/purescript/purescript-free/blob/9999ba1a07f26b440a91155717b2988a88d00bcb/src/Control/Monad/Free.purs
code:purs(hs)
-- Type
data Free f a = Free (FreeView f Val Val) (CatList (ExpF f))
data FreeView f a b = Return a
| Bind (f b) (b -> Free f a)
newtype ExpF f = ExpF (Val -> Free f Val)
data Val
-- Functor
instance Functor (Free f) where
map f k = f <$> k
-- Apply
instance Apply (Free f) where
apply = ap
-- Bind
instance Bind (Free f) where
bind (Free v s) k = Free v (snoc s (ExpF (unsafeCoerceBind k)))
where
unsafeCoerceBind :: forall a b. (a -> Free f b) -> Val -> Free f Val
unsafeCoerceBind = unsafeCoerce
-- Applicative
instance Applicative (Free f) where
pure = fromView <<< Return
-- Monad
instance Monad (Free f)
データ型が複雑になっている理由を解明したい #??
https://zenn.dev/funnycat/articles/8660a8baccd5fc
ここにかいてた
Free (f (Free f a))って、Free メイン (続きの計算)ってかんじなのか?
どのへんが「Free」なのか
https://fumieval.hatenablog.com/entry/20121111/1352614885
https://blog.functorial.com/posts/2012-07-22-What-Makes-The-Free-Monad-Free.html
https://viercc.github.io/blog/posts/2022-03-11-maybe-is-free.html
Maybe == Free Proxy
Free fだけでなく、Freeそれ自体もmonad
https://qiita.com/hennin/items/c02ae17cac00494a0f56
Freeモナドって何なのさっ!? - capriccioso String Creating(Object something){ return My.Expression(something); }
code:hs
data MyProgram n = Old Int n | Hey n | Hello n | GoodBye
instance Functor MyProgram where
fmap f (Old o n) = Old o (f n)
fmap f (Hey n) = Hey (f n)
fmap f (Hello n) = Hello (f n)
fmap f GoodBye = GoodBye
runProgram :: Show r => Free MyProgram r -> IO ()
runProgram (Free (Old o n)) = putStrLn ("I'm " ++ show o ++ " years old") >> runProgram n
runProgram (Free (Hey n)) = putStrLn "Hey!!!" >> runProgram n
runProgram (Free (Hello n)) = putStrLn "Hello!!!" >> runProgram n
runProgram (Free GoodBye) = putStrLn "GoodBye!!!"
runProgram (Pure r) = putStrLn $ "return " ++ show r
pg :: Free MyProgram ()
pg = do
Free $ Hey (Pure ())
Free $ Hello (Pure ())
Free $ Old 2 (Pure ())
Free GoodBye
main :: IO ()
main = runProgram pg
不動点との関係
data Fix f = Fix (f (Fix f))
https://ilyaletre.hatenablog.com/entry/2018/03/20/224320
https://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html
todos
pursのFreeの構成の意味
Stackless Scala With Free Monads
Stack Safety for Free
Trampolineモナド
FreeT
TrampolineモナドとFreeTどっちを使うのか
コンパイル後JSでどんなふうに変わっているのか観察
関連
Data.CatList
Control.Monad.Free.Church
処理効率を考えて実装されたFreeモナド
Control.Monad.Free.TH
ASTのデータ型からFreeのアクションを導出する
Coyoneda
Cofree
Operationalモナド
Trampolineモナド
Idetalモナド
Implicit Recursive Slowdown
自由モノイド
自由マグマ
Freerモナド
Free Applicative Functor
Reflection without remorse
/haskell-shoen/Freeモナド
http://okmij.org/ftp/Haskell/zseq.pdf
Asymptotic Improvement of Computations over Free Monads
論文
https://www.researchgate.net/publication/221440256_Asymptotic_Improvement_of_Computations_over_Free_Monads
ユースケース
Trampolineモナド
2Dゲーム
https://hackage.haskell.org/package/free-game
https://fumieval.hatenablog.com/entry/2013/01/09/211215
DSLを作る
http://lqtmirage.hatenablog.com/entry/2018/06/18/182130
https://qiita.com/hiruberuto/items/3d55b0e54565dbb286a7
DSLがいまいちピンときていないmrsekut.icon
Parsecみたいに、Parser ..という型で区切られた世界のこととかをそう呼んでるのか?
普通に型で区切られた世界とどう違うのか
あ、普通にMonadを作ることをDSL作成と言ってるだけか
「Monadを作ること」が「DSLを作ること」にほぼ同じなのであれば、「Freeモナドを作ることの嬉しさ」として「DSLを作れること」って挙げるのおかしくない?
あくまでも「Freeモナドの嬉しさ」は「簡単にモナドを作れること」であって、
「DSLを作ること」は「Monadの嬉しさ」の一つに過ぎないだろう
でもまあFree型はMonadをdata構造として表現したものだから、「Freeモナドの嬉しさ」は「Monadの嬉しさ」と表裏一体だから、結局は同じことなのか。たしかに。
Haskellは言語内DSLを書くのに優れている
Monad構造を動的に操作する例
Free型はMonadをdata構造として表現したものなので、Lispのように同図像性を、型安全に持ったことになる
それを使ってデータの操作をした有用な例みたいなのを見てみたい
ps
https://pursuit.purescript.org/packages/purescript-free/4.0.0/docs/Control.Monad.Free#t:Free
https://github.com/purescript/purescript-free
/herp-technote/Free monad
/haskell-shoen/Freeモナド
https://serokell.io/blog/introduction-to-free-monads
https://monadplus.pro/haskell/2022/04/19/free-interpreter/
https://viercc.github.io/blog/posts/2020-08-23-fmonad.html
chrome-extension://oemmndcbldboiebfnladdacbdfmadadm/http://functorial.com/stack-safety-for-free/index.pdf
https://pursuit.purescript.org/packages/purescript-free/6.0.1/docs/Control.Monad.Trampoline
https://github.com/purescript/purescript-free
chrome-extension://oemmndcbldboiebfnladdacbdfmadadm/http://functorial.com/stack-safety-for-free/index.pdf
https://eed3si9n.com/herding-cats/ja/tail-recursive-monads.html
https://uid0130.blogspot.com/2013/07/trampoline.html
https://eed3si9n.com/herding-cats/ja/stackless-scala-with-free-monads.html
https://eed3si9n.com/learning-scalaz/ja/Stackless+Scala+with+Free+Monads.html
https://halcat.org/scala/stackless/index.html
https://qiita.com/dashiishida/items/c20d6e08a260e3ce0f64
https://free.cofree.io/2017/08/24/trampoline/
https://qiita.com/yyu/items/f1601749097ba3ee03db
https://niyarin.github.io/contents/2019-12/my-scm-plan.html
https://gitter.im/purescript/purescript?at=572939c172798bd77be9c0f0
https://qiita.com/torao@github/items/4b224489c71bf460ad7c
chrome-extension://oemmndcbldboiebfnladdacbdfmadadm/https://blog.higher-order.com/assets/trampolines.pdf
https://scrapbox.io/herp-technote/Free_monad
https://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html
https://fumieval.hatenablog.com/entry/20121111/1352614885
https://qiita.com/YoshikuniJujo/items/c71644b5af1f5195cbf3
https://blog.functorial.com/posts/2012-07-22-What-Makes-The-Free-Monad-Free.html
https://github.com/shiatsumat/wiwinwlh-jp/wiki/%E9%AB%98%E5%BA%A6%E3%81%AA%E3%83%A2%E3%83%8A%E3%83%89#free-monads
https://zenn.dev/zecl/books/d535208d9f6176/viewer/93d6b0
https://ro-che.info/articles/2013-03-31-flavours-of-free-applicative-functors
https://elvishjerricco.github.io/2016/04/08/applicative-effects-in-free-monads.html
https://qiita.com/goldarn_ring/items/1c895292ae313b74a410
http://lotz84.github.io/haskell/free-monad.html
https://scrapbox.io/haskell-shoen/Free%E3%83%A2%E3%83%8A%E3%83%89
http://comonad.com/reader/2008/monads-for-free/
https://leanpub.com/purescript/read#leanpub-auto-domain-specific-languages
https://qiita.com/hiruberuto/items/3d55b0e54565dbb286a7
https://eng-blog.iij.ad.jp/archives/3467
https://pursuit.purescript.org/packages/purescript-free/4.0.1/docs/Control.Monad.Free#t:Free
https://pursuit.purescript.org/packages/purescript-catenable-lists/6.0.1/docs/Data.CatList#t:CatList
https://github.com/purescript/purescript-catenable-lists
https://twitter.com/hennin_ltn/status/1189295895382241285
https://zenn.dev/lotz/articles/56671def583c58612361
実用例