免費 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 fMonad,每當 fFunctor 時,我們可以使用標準的 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