foldFree 和 iterM 如何工作
有一些函式可以幫助拆除 Free 計算,將它們解釋為另一個 monad m:iterM :: (Functor f, Monad m) => (f (m a) -> m a) -> (Free f a -> m a) 和 foldFree::Monad m => (forall x. f x -> m x) -> (Free f a -> m a)。他們在做什麼?
首先讓我們看看如何手動將 Teletype a 函式解釋為 IO。我們可以看到 Free f a 被定義了
data Free f a
= Pure a
| Free (f (Free f a))
Pure 案例很簡單:
interpretTeletype::Teletype a -> IO a
interpretTeletype (Pure x) = return x
interpretTeletype (Free teletypeF) = _
現在,如何解釋用 Free 建構函式構建的 Teletype 計算?我們想通過檢查 teletypeF::TeletypeF (Teletype a) 來得到 IO a 的值。首先,我們將編寫一個函式 runIO::TeletypeF a -> IO a,它將單個自由 monad 層對映到 IO 動作:
runIO::TeletypeF a -> IO a
runIO (PrintLine msg x) = putStrLn msg *> return x
runIO (ReadLine k) = fmap k getLine
現在我們可以用 runIO 來填補 interpretTeletype 的其餘部分。回想一下 teletypeF::TeletypeF (Teletype a) 是 TeletypeF 仿函式的一層,它包含了 Free 計算的其餘部分。我們將使用 runIO 來解釋最外層(所以我們有 runIO teletypeF::IO (Teletype a))然後使用 IO monad 的 >>= 組合來解釋返回的 Teletype a。
interpretTeletype::Teletype a -> IO a
interpretTeletype (Pure x) = return x
interpretTeletype (Free teletypeF) = runIO teletypeF >>= interpretTeletype
foldFree 的定義只是 interpretTeletype 的定義,除了 runIO 函式已被考慮在內。因此,foldFree 獨立於任何特定的基礎仿函式和任何目標 monad 工作。
foldFree::Monad m => (forall x. f x -> m x) -> Free f a -> m a
foldFree eta (Pure x) = return x
foldFree eta (Free fa) = eta fa >>= foldFree eta
foldFree 的等級為 2:eta 是一種自然變形。我們本來可以給 foldFree 一種 Monad m => (f (Free f a) -> m (Free f a)) -> Free f a -> m a,但是這樣可以讓 eta 自由地檢查 f 層內的 Free 計算。賦予 foldFree 這種更具限制性的型別可確保 eta 一次只能處理單個層。
iterM 確實賦予摺疊功能檢查子計算的能力。上一次迭代的(monadic)結果可用於 f 的引數中的下一個。iterM 類似於一個 paramorphism, 而 foldFree 就像一個 catamorphism 。
iterM :: (Monad m, Functor f) => (f (m a) -> m a) -> Free f a -> m a
iterM phi (Pure x) = return x
iterM phi (Free fa) = phi (fmap (iterM phi) fa)