【问题标题】:How to derive multiple Functor instances in Haskell?如何在 Haskell 中派生多个 Functor 实例?
【发布时间】:2019-01-14 05:04:28
【问题描述】:

我正在为表达式定义一个 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 over a2,但是如果我想fmap over a1r,我发现它不可能将 DeriveFunctornewtype 一起使用,因为以下代码不起作用:

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

deriving instance Functor (ExprR a1 a2)

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

如果我们需要的不仅仅是Functor,该怎么办?考虑FoldableTraversable等。

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

【问题讨论】:

  • 仅供参考:在相当新的 GHC 版本中,DeriveTraversable 意味着 DeriveFunctorDeriveFoldable
  • Csongor Kiss 做了一些非常聪明的工作,用泛型做你想做的事情。我相信其他人也做过相关的事情。这些都不太适合当前的 GHC 仿制药系统。我想该系统将在未来几年内被替换。您可能可以使用 Template Haskell 找到其他解决方案。

标签: haskell functor bifunctor


【解决方案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 的预期含义,但我怀疑您确实想这样做。排除数据类型的一致性可以显着清理其余代码:它可以梳理出隐藏的泛化机会,揭示您所依赖的标准结构,并通过更多地依赖标准库来减少样板函数的数量。

【讨论】:

  • 排除伴随每个案例的相同数据的冗余。但是既然你提到了它,我想App FunName [(ExprF r, a)] 或类似的就足够了。关于性能的有趣点,我通常不担心非渐近性能的事情。
  • 是的,我最终看到了您试图获得的冗余。另一种选择:代替(a1, a2),使用(,,) a1 a2,将函子作为参数。不过不太适合推导。
  • 这只是一个用于解释我的问题的示例,实际情况比这更复杂,无法通过使用元组来简化,感谢您的建议,但这不是我的意思。
猜你喜欢
  • 1970-01-01
  • 2013-09-03
  • 2012-09-16
  • 2018-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多