如何在Haskell中派生多个Functor实例?

时间:2019-01-14 05:04:29

标签: haskell functor bifunctor

我正在为表达式定义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)

现在使用DeriveFunctor只能帮助我定义instance Functor (Expr r a1),因此可以在fmap上使用a2,但是如果我想在{{ 1}}或fmap,我发现无法将a1r一起使用,因为以下代码不起作用:

DeriveFunctor

如果我只需要两个类型参数,那么newtype可能是一个好主意,确实有一些软件包可以提供newtype ExprR a1 a2 r = MkExprR { getExpr :: Expr r a1 a2 } deriving instance Functor (ExprR a1 a2) ,但是如果我们需要三个呢?我们需要Bifunctor还是DeriveBifunctor等吗?

而且,如果我们需要的数量超过DeriveTrifunctor,该怎么办?考虑使用DeriveQuadfunctorFunctor

关于此问题有解决方案吗?人们如何在Haskell实践中解决这个问题?

1 个答案:

答案 0 :(得分: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)

现在您所需要的只是bifunctor。是否要执行此操作取决于a1a2的预期含义,但是我怀疑您确实希望执行此操作。在数据类型中考虑统一性可以显着清理其余代码:它可以消除隐藏的泛化机会,揭示您所依赖的标准结构,并通过更多地依赖标准库来减少样板函数的数量。 / p>