Generics 如何将'local'和'Reader'单子与废弃样板(SYB)一起使用?

Generics 如何将'local'和'Reader'单子与废弃样板(SYB)一起使用?,generics,haskell,ghc,Generics,Haskell,Ghc,如何使用SYB(或其他Haskell泛型包)在读取器monad中编写转换,使用本地修改子计算环境?GenericM和everywhereM(带a->ma)的类型似乎不支持使用local(类型ma->ma)来包装子计算。如果可能的话,我想要一个使用“标准”/“现成”转换的解决方案 有代表性的例子 (神秘的)递归数据类型: {-# LANGUAGE DeriveDataTypeable , Rank2Types , ViewPatterns #-} import Data.Generics impo

如何使用SYB(或其他Haskell泛型包)在
读取器
monad中编写转换,使用
本地
修改子计算环境?
GenericM
everywhereM
(带
a->ma
)的类型似乎不支持使用
local
(类型
ma->ma
)来包装子计算。如果可能的话,我想要一个使用“标准”/“现成”转换的解决方案

有代表性的例子 (神秘的)递归数据类型:

{-# 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)
一种递归函数,用值递增所有嵌入的
Int
s 大于包装它们的
活页夹的数量:

-- 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
这个例子的要点是:

  • (:@)
    案例是典型的样板递归:
    在任何地方
    都自动工作
  • Var
    案例使用该环境,但不修改它:
    m
    在这里工作的每个地方,通过将
    mkM
    应用于特定于
    Var
    案例的
    Exp->Reader Int Exp
  • Lam
    案例在递归之前修改了环境:
    everywhere
    在这里不起作用(据我所知)。
    Binder
    类型告诉我们需要在哪里使用
    local
    ,因此我们可能希望将
    mkM
    应用于
    Binder Exp->Reader Int(Binder Exp)
    特定情况,但我不知道如何使用

,包括上面的代码。

这里有一个解决方案,通过创建一个新的SYB遍历,
everywhereMM

newtype MM m x = MM { unMM :: m x -> m x }
mkMM :: (Typeable a , Typeable b) => (m a -> m a) -> m b -> m b
mkMM t = maybe id unMM (gcast (MM t))

-- Apply a 'GenericM' everywhere, transforming the results with a
-- 'GenericMM'.
type GenericMM m = Data a => m a -> m a
everywhereMM :: Monad m => GenericMM m -> GenericM m -> GenericM m
everywhereMM mm m x = mm (m =<< gmapM (everywhereMM mm m) x)
但是这个解决方案有些不令人满意:创建新的遍历需要深入挖掘SYB库代码

同样,包括上述代码

newtype MM m x = MM { unMM :: m x -> m x }
mkMM :: (Typeable a , Typeable b) => (m a -> m a) -> m b -> m b
mkMM t = maybe id unMM (gcast (MM t))

-- Apply a 'GenericM' everywhere, transforming the results with a
-- 'GenericMM'.
type GenericMM m = Data a => m a -> m a
everywhereMM :: Monad m => GenericMM m -> GenericM m -> GenericM m
everywhereMM mm m x = mm (m =<< gmapM (everywhereMM mm m) x)
type W = Reader Int
weaken3 :: Exp -> Exp
weaken3 e = runReader (w e) 0
  where
  w :: GenericM W
  w = everywhereMM (mkMM b) (mkM v)

  b :: W (Binder Exp) -> W (Binder Exp)
  b = local succ

  v :: Exp -> W Exp
  v (Var j) = do
    i <- ask
    return $ wVar i j
  v e = return e