gpt4 book ai didi

generics - 如何在 Scrap Your Boilerplate (SYB) 中使用 `local` 和 `Reader` monad?

转载 作者:行者123 更新时间:2023-12-04 12:33:03 24 4
gpt4 key购买 nike

如何使用 SYB(或其他一些 Haskell 泛型包)在 Reader 中编写转换使用 local 的 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)

递增所有嵌入的递归函数 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)

例子的要点:
  • (:@) case 是典型的样板递归:everywhereM自动在这里工作。
  • Var case 使用环境,但不修改它:everywhereM在这里工作,通过申请 mkMExp -> Reader Int Exp特定于 Var案子。
  • Lam case 在递归之前修改环境:everywhereM在这里不起作用(据我所知)。 Binder type 告诉我们需要在哪里使用 local , 以便我们希望申请 mkMBinder Exp -> Reader Int (Binder Exp)具体情况,但我不知道如何。

  • Here is a Gist with more examples ,包括上面的代码。

    最佳答案

    这是通过创建新的 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是相似的
    everywhereMmkM Data.Generics.Schemes Data.Generics.Aliases ;区别在于 everywhereMM使用 mm , 一个 GenericMM , 转换结果。

    现在我们可以写一个 weaken3 .这个想法是结合标准 GenericM对于 Var的情况 Exp带有新的 GenericMM适用 localBinder案子:
    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 库代码。

    再次, here is a Gist with more examples ,包括上面的代码。

    关于generics - 如何在 Scrap Your Boilerplate (SYB) 中使用 `local` 和 `Reader` monad?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16806162/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com