免费 monad 将 monadic 计算分为数据结构和解释器
例如,涉及从提示读取和写入的命令的计算:
首先,我们将计算的命令描述为 Functor 数据类型
{-# LANGUAGE DeriveFunctor #-}
data TeletypeF next
= PrintLine String next
| ReadLine (String -> next)
deriving Functor
然后我们使用 Free 创建“免费 Monad over TeletypeF”并构建一些基本操作。
import Control.Monad.Free (Free, liftF, iterM)
type Teletype = Free TeletypeF
printLine::String -> Teletype ()
printLine str = liftF (PrintLine str ())
readLine::Teletype String
readLine = liftF (ReadLine id)
由于 Free f 是 Monad,每当 f 是 Functor 时,我们可以使用标准的 Monad 组合器(包括 do 表示法)来构建 Teletype 计算。
import Control.Monad -- we can use the standard combinators
echo::Teletype ()
echo = readLine >>= printLine
mockingbird::Teletype a
mockingbird = forever echo
最后,我们写了一个解释器,将 Teletype a 值转化为我们知道如何使用 IO a 的东西
interpretTeletype::Teletype a -> IO a
interpretTeletype = foldFree run where
run::TeletypeF a -> IO a
run (PrintLine str x) = putStrLn *> return x
run (ReadLine f) = fmap f getLine
我们可以用来运行Teletype a 中的 Teletype a 计算
> interpretTeletype mockingbird
hello
hello
goodbye
goodbye
this will go on forever
this will go on forever