在表达式

时间:2017-11-14 14:35:03

标签: haskell functor generic-programming scrap-your-boilerplate

假设我们有以下数据结构,其值代表某种语言的表达式:

data BExpr = Stop
           | Guard Expr BExpr
           | Choice BExpr BExpr
           | Parallel BExpr BExpr
           | Disable BExpr BExpr
           -- ... and so on.
           | Act Expr


data Expr = Var String | Val Int

我想定义一个函数substBExpr,用BExpr中的整数值替换变量名。这可以按如下方式完成:

subst :: Map String Int -> Expr -> Expr
subst substMap e@(Var name) = maybe e Val $ Map.lookup name substMap
subst _  v@(Val _)          = v

substBExpr :: Map String Int -> BExpr -> BExpr
substBExpr _ Stop                     =  Stop
substBExpr substMap (Guard gExp be)   = Guard gExp' be'
    where gExp' = subst substMap gExp
          be' = substBExpr substMap be
substBExpr substMap (Choice be0 be1)  = Choice be0' be1'
    where be0' = substBExpr substMap be0
          be1' = substBExpr substMap be1
substBExpr substMap (Parallel be0 be1) = Parallel be0' be1'
    where be0' = substBExpr substMap be0
          be1' = substBExpr substMap be1
substBExpr substMap (Disable be0 be1) = Disable be0' be1'
    where be0' = substBExpr substMap be0
          be1' = substBExpr substMap be1
substBExpr substMap (Act aExp)        = Act aExp'
    where aExp' = subst substMap aExp

问题是这需要很多样板,我想避免使用。

我现在可以想到两种解决方案。第一个解决方案是将类型参数添加到BExpr,以便我可以为其自动派生Functor的实例:

data GBExpr e = GStop
              | GGuard e (GBExpr e)
              | GChoice (GBExpr e) (GBExpr e)
              | GParallel (GBExpr e) (GBExpr e)
              | GDisable (GBExpr e) (GBExpr e)
              -- ... and so on.
              | GAct e
              deriving (Show, Functor)

type BExpr2 = GBExpr Expr

substBExpr2 :: Map String Int -> BExpr2 -> BExpr2
substBExpr2 substMap = (subst substMap <$>)

虽然这有效,但如果Bexpr无法更改为包含类型参数,则不可行。

第二个解决方案是使用gfoldl

substT :: (Typeable a) => Map String Int -> a -> a
substT substMap = mkT (subst substMap)

substExprs :: (Data a) => Map String Int -> a -> a
substExprs substMap a = runIdentity $ gfoldl step return (substT substMap a)
    where
      step :: Data d => Identity (d -> b) -> d -> Identity b
      step cdb d = cdb <*> pure (substExprs substMap d)

其中涉及一些自己的样板(但我不知道遍历结构的任何函数,并且只有在它们可以转换为特定类型时才将函数应用于结构内的值)。

是否有更好的替代方案&#34;废弃你的样板&#34;问题? (我可以想象它可能是一些涉及镜头的解决方案,但我并不熟悉光学......)。

0 个答案:

没有答案