免费 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