【发布时间】:2013-05-29 05:30:49
【问题描述】:
如何使用 SYB(或其他一些 Haskell 泛型包)在使用 local 修改子计算环境的 Reader monad 中编写转换? GenericM 和 everywhereM(带有 a -> m a)的类型似乎不支持使用 local(m a -> m a 类型)来包装子计算。如果可能的话,我想要一个使用“标准”/“现成”转换的解决方案。
代表性例子
一种(神秘的)递归数据类型:
{-# LANGUAGE DeriveDataTypeable , Rank2Types , ViewPatterns #-}
import Data.Generics
import Control.Applicative
import Control.Monad.Reader
import Control.Arrow
data Exp = Var Int | Exp :@ Exp | Lam (Binder Exp)
deriving (Eq , Show , Data , Typeable)
newtype Binder a = Binder a
deriving (Eq , Show , Data , Typeable)
一个递归函数,它用值递增所有嵌入的Ints
大于包裹它们的Binders 的数量:
-- Increment all free variables:
-- If G |- e:B then G,A |- weaken e:B.
weaken1 :: Exp -> Exp
weaken1 = w 0
where
w :: Int -> Exp -> Exp
-- Base case: use the environment ('i'):
w i (Var j) = wVar i j
-- Boilerplate recursive case:
w i (e1 :@ e2) = w i e1 :@ w i e2
-- Interesting recursive case: modify the environment:
w i (Lam (Binder e)) = Lam (Binder (w (succ i) e))
wVar :: Int -> Int -> Exp
wVar i j | i <= j = Var (succ j)
| otherwise = Var j
目标是将i 参数放到weaken1 环境中,并使用SYB 自动处理(:@) 的样板递归案例。
使用Reader 环境重写weaken1,但不使用SYB:
weaken2 :: Exp -> Exp
weaken2 e = runReader (w e) 0
where
w :: Exp -> Reader Int Exp
w (Var j) = do
i <- ask
return $ wVar i j
w (e1 :@ e2) = (:@) <$> w e1 <*> w e2
w (Lam (Binder e)) = Lam . Binder <$> local succ (w e)
例子的重点:
-
(:@)案例是典型的样板递归:everywhereM在这里自动工作。 -
Var案例使用环境,但不修改它:everywhereM在这里工作,通过将mkM应用于特定于Var案例的Exp -> Reader Int Exp。 -
Lam案例在递归之前修改了环境:everywhereMnot 在这里工作(据我所知)。Binder类型告诉我们需要在哪里使用local,因此我们可能希望将mkM应用于Binder Exp -> Reader Int (Binder Exp)特定情况,但我不知道如何。
Here is a Gist with more examples,包括上面的代码。
【问题讨论】: