7

如何使用 SYB(或其他一些 Haskell 泛型包)在用于修改子计算环境的Readermonad 中编写转换?和(with )local的类型似乎不支持使用(of type ) 来包装子计算。如果可能的话,我想要一个使用“标准”/“现成”转换的解决方案。GenericMeverywhereMa -> m alocalm 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)

一个递归函数,它将所有嵌入Int的 s 递增,其值大于Binder包装它们的 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

目标是将i参数weaken1放入Reader环境中并使用 SYB 自动处理(:@).

重写weaken1,使用Reader环境,但不是 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特定Exp -> Reader Int ExpVar案例。
  • 案例在Lam递归之前修改了环境:everywhereM这里不起作用(据我所知)。类型告诉我们需要在哪里使用,以便我们可能希望应用于特定情况,但我不知道如何。BinderlocalmkMBinder Exp -> Reader Int (Binder Exp)

这是一个包含更多示例的 Gist,包括上面的代码。

4

1 回答 1

4

这是通过创建新的 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)

everywhereMM和的定义mkMM类似于everywhereMandmkM中的Data.Generics.Schemesand Data.Generics.Aliases;不同之处在于everywhereMM使用mm, a GenericMM, 来转换结果。

现在我们可以写一个weaken3. 这个想法是将案例的标准与 适用GenericM于案例的新标准结合起来:VarExpGenericMMlocalBinder

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

但是这个解决方案有些不尽人意:创建新的遍历需要深入挖掘 SYB 库代码。

同样,这里有一个包含更多示例的 Gist,包括上面的代码。

于 2013-05-29T05:30:49.363 回答