【问题标题】:The relationship between type classes - dependency vs using instance类型类之间的关系——依赖与使用实例
【发布时间】:2015-05-24 07:48:05
【问题描述】:

假设我想为 semigroup 和 monoid 定义自己的类型类。所以我写了这段代码:

class Semigroup g where
    (<>) :: g -> g -> g

class Semigroup m => Monoid m where
    mempty :: m

但是还有另一种方法可以定义这些类型类之间的关系,并带有一些扩展:

class Semigroup g where
    gappend :: g -> g -> g

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m

instance Monoid m => Semigroup m where
    gappend = mappend

后一种设计有一个优势——稍后我可以为 Monoid 添加更多实例。例如,如果我有一个向量空间的类型类,我以后可以将它变成一个加法组,而不必在类声明中指定它。另一方面,我不得不使用灵活的实例和不可判定的实例。

我的问题是 - 对于这种特殊情况,最好的设计是什么?

【问题讨论】:

  • better 在这里是一个非常困难的术语——我的问题是:你为什么要重新定义它?话虽如此:我更喜欢第一个(不确定的实例是 IMO 的危险信号)
  • 我想编写自己的线性代数库,并希望有一些舒适的类,例如向量空间和环。 Monoid 和 Semigroup 仅用作示例。
  • 是的,我猜是公平的——但这对于 SO 来说确实不是最好的问题(你可能会得到一些关闭的标志,因为从表面上看它似乎有点基于意见)——也许一个更好的主意是去做 reddit 或 haskell IRC 频道/邮件列表来讨论这个(?) - 特别是在后面,你会有很多非常聪明的人会立即告诉你为什么一个或另一个是 要走的路;)
  • 实在看不出有什么优势。您能否准确说明第二个代码相对于第一个代码的收益是什么,但代价是允许无法确定的实例?
  • 在我看来,灵活且不可判定的实例在实践中是无害的。但是,如果您想要一些不是幺半群的半群,第二种方法需要重叠实例,而 that 是真正痛苦的来源。重叠和不连贯的实例具有非常微妙的语义,难以掌握,很容易导致意外。

标签: haskell typeclass


【解决方案1】:

第一个版本说“monoids 是半群,具有附加属性 mempty”。

第二个肯定说“所有类型都是半群,只要它们也是幺半群”。除非您乐于打开重叠实例,否则您不能添加任何其他实例,因此这相当于“当且仅当类型是幺半群时,它才是半群”;与真正的关系完全相反。

我几乎总是更喜欢前者。是的,它迫使想要添加Monoid 实例的人也编写Semigroup 实例,但他们必须做的真正“工作”是相同的:决定memptymappend 的实现。通过使用第二种方法,您唯一可以避免它们的是一些样板文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2016-05-16
    • 2017-01-07
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 2012-04-06
    相关资源
    最近更新 更多