Haskell免费monad将monadic计算拆分为数据结构和解释器

示例

例如,涉及从提示符读取和写入命令的计算:

首先,我们将计算的“命令”描述为Functor数据类型

{-# LANGUAGE DeriveFunctor #-}

data TeletypeF next
    = PrintLine String next
    | ReadLine (String -> next)
    deriving Functor

然后我们使用Free创建“ 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计算。

importControl.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计算IO

> interpretTeletype mockingbird
hello
hello
goodbye
goodbye
this will go on forever
this will go on forever