4

我正在为表达式定义一个 AST,它具有三个类型参数,如下所示:

{-# language DeriveFunctor, DeriveFoldable, DeriveTraversable #-}

-- | a general represetation of an expression
-- , with ref info contained in r and two attributes contained in a1, a2
data Expr r a1 a2
    = Ref r a1 a2
    | App FunName [Expr r a1 a2] a1 a2
    | Lit Value a1 a2
    deriving (Functor, Foldable, Traversable)

现在 usingDeriveFunctor只能帮助我定义instance Functor (Expr r a1),所以我可以fmapover a2,但是如果我想fmapover a1or r,我发现无法使用DeriveFunctora newtype,因为以下代码不起作用:

newtype ExprR a1 a2 r = MkExprR { getExpr :: Expr r a1 a2 }

deriving instance Functor (ExprR a1 a2)

如果我只需要两个类型参数,那么Bifunctor可能是个好主意,并且确实有一些包提供DeriveBifunctor,但是如果我们需要三个呢?我们需要DeriveTrifunctorDeriveQuadfunctor吗?

而且,如果我们需要更多Functor呢?考虑FoldableTraversable_

这个问题有什么解决办法吗?人们如何在haskell实践中解决这个问题?

4

1 回答 1

1

不是直接回答你的问题,而是观察。如果这是您正在使用的真实类型,它可以使用一些分解。 a1a2使用相同,每个节点使用一次。您可以考虑这一点。

data ExprNode r a = ExprNode (ExprF r a) a
    -- it's a bifunctor

data ExprF r a
    = Ref r
    | App FunName [ExprNode r a]
    | Lit Value
    -- it's a bifunctor

type Expr r a1 a2 = ExprNode r (a1, a2)

现在你需要的只是双函子。您是否要这样做取决于您的预期含义a1a2是什么,但我怀疑您确实想要这样做。消除数据类型的一致性可以显着清理其余代码:它可以梳理出隐藏的泛化机会,揭示您所依赖的标准结构,并通过更多地依赖标准库来减少样板函数的数量。

于 2019-01-14T15:33:22.203 回答