> {-# LANGUAGE DeriveFunctor #-} > import Control.Monad 显然,[有个 free monad 的库](https://hackage.haskell.org/package/free)。对,又是 Edward Kmett。 > import Control.Monad.Free > import Prelude.Extras (Show1) 这是我们的二叉树 > data B a = B a a > deriving (Functor, Show) > instance Show1 B > type Tree = Free B > leaf :: a -> Tree a > leaf = Pure > fork :: Tree a -> Tree a -> Tree a > fork a b = Free (B a b) 看起来不错。 ghci> fork (leaf 2) (leaf 3) Free (B (Pure 2) (Pure 3)) 我们随便用一下子 `(>>=)` ghci> fork (leaf 2) (leaf 3) >>= (\x -> fork (leaf $ 5 * x) (leaf $ 6 * x)) Free (B (Free (B (Pure 10) (Pure 12))) (Free (B (Pure 15) (Pure 18)))) 发生了什么?

Monads for substitution

Monads provide substitution (fmap) and renormalization (join) — Edward Kmett
观察表明,`(>>=)` 将每个 `leaf x` 替换成了 `fork (leaf $ 5 * x) (leaf $ 6 * x)`。替换成什么,显然是 `(>>=)` 的第二个参数决定的。所以我们得出,在 free monad 中,`(>>=)` 的作用是:将每个 `Pure x` 替换成 `f x`。 instance Functor f => Monad (Free f) where return = Pure Pure a >>= f = f a Free m >>= f = Free ((>>= f) <$> m) 将 `>>=` 读作“替换”之后,这就变得非常显然了。

Monad 的定律

return a >>= f = f a 这不废话么,`Pure a` 替换成 `f a` m >>= return = m `Pure a` 替换成 `Pure a`。也挺对的。 (m >>= f) >>= g = m >>= (\x -> f x >>= g) 这里其实就是进行了两个变换 `f` 和 `g`。这两个变换至于是先在 m 里替换一次 f,再替换一次 `g`,还是直接把 `m` 里的每个 `Pure` 都替换成了 `f` `g` 的组合,是没有关系的,因为第一次替换完后的所有 `Pure` 都是从 `f` 里面来的。

变量的替换

考虑一件事:我们用一个 monad `AST` 来存一个表达式,`AST a` 的 `a` 存的是所有未被绑定的变量。 它还有可能会在一个子表达式里面绑定一个变量。考虑: > data Var a = X | U a > deriving (Show, Functor) AST a AST (Var a) 之间的关系。后者的未被绑定的变量,有 `X` 和 `U a` 两种。我们完全可以假装`AST (Var a)` 是 `AST a` 被绑定了一个变量的版本。其中,这个所谓的未绑定的变量 `X` 在这里被绑定,剩余的未绑定的变量变成 `U a`。 > data AST a > = FV a > | AST a :@ AST a > | Lam (AST (Var a)) > deriving (Functor, Show) > instance Applicative AST where > pure = FV > (<*>) = ap AST 如何是个 monad 呢? > instance Monad AST where > FV a >>= f = f a > Lam m >>= f = Lam (m >>= go) > where go X = return X > go (U r) = U <$> (f r) `AST a` 中的 `a` 是所有未被绑定的变量,所以在 `(>>=)` 中我们把绑定了的变量单独考虑就好了。 还差一个: > (m :@ n) >>= f = (m >>= f) :@ (n >>= f) 这样,比如我们想绑定一个变量 > abstract :: Eq a => a -> AST a -> AST (Var a) > abstract v e = go <$> e > where go x = if x == v then X else U x 或者把当前绑定的变量替换成一个表达式? > instantiate :: AST a -> AST (Var a) -> AST a > instantiate m e = e >>= go > where go X = m > go (U x) = return x > lam :: Eq a => a -> AST a -> AST a > lam v e = Lam (abstract v e) ghci> abstract "a" (FV "a" :@ FV "b") FV X :@ FV (U "b") ghci> instantiate (FV "x" :@ FV "y") it -- it 指的是上一个表达式的值哦 (FV "x" :@ FV "y") :@ FV "b" ghci> lam "x" it Lam ((FV X :@ FV (U "y")) :@ FV (U "b")) it :: AST [Char] (0.01 secs, 175,712 bytes) ghci> lam "y" it Lam (Lam ((FV X :@ FV (U X)) :@ FV (U (U "b")))) it :: AST [Char] (0.01 secs, 184,816 bytes) ghci> lam "b" it Lam (Lam (Lam ((FV X :@ FV (U X)) :@ FV (U (U X))))) 对于一个变量啊,`X` 表示它在当前层被绑定,`U` 表示它在当前层没被绑定。比如你看最后那个 `(U (U X))` 是不是在最后一层绑定的? 这叫 [bound](https://hackage.haskell.org/package/bound)。又是 Edward Kmett 写的。不过他写的有一个 abstract 的懒标记功能,提高了性能。 monad 原来还能干这事啊 [代码在此](index.lhs)