Haskell中的孤立实例

当使用-Wall选项编译我的Haskell应用程序时,GHC会抱怨孤立的实例,例如:

Publisher.hs:45:9:
    Warning: orphan instance: instance ToSElem Result

Type类ToSElem不是我的,它由HStringTemplate定义。

现在我知道如何解决这个问题(将实例声明移到声明Result的模块中),我知道为什么GHC宁愿避免孤立的实例,但我仍然相信我的方式更好。 我不在乎编译器是否不方便 - 而不是我。

我想在Publisher模块中声明我的ToSElem实例的原因是因为它是依赖于HStringTemplate的发布者模块,而不是其他模块。 我试图保持关注的分离并避免每个模块都依赖于HStringTemplate。

我认为,与Java接口相比,Haskell的类型类的优点之一是它们是开放的而不是封闭的,因此实例不必与数据类型在同一位置声明。 GHC的建议似乎是忽视这一点。

所以,我所寻找的是要么证明我的想法是合理的,并且我会无视/压制这个警告是有道理的,或者说是一个更有说服力的论点来反对我的做法。


我明白你为什么要这样做,但不幸的是,Haskell类似乎像你说的那样“开放”,可能只是一种幻想。 许多人认为这样做的可能性是Haskell规范中的一个错误,因为我将在下面解释。 无论如何,如果它是真的不适合你需要申报或者在类被声明或者类型声明的模块中的模块中的情况下,这可能是你应该使用一个标志newtype或其他一些包装你的类型。

孤儿实例需要被避免的原因比编译器的便利性要深入得多。 这个话题颇具争议性,你可以从其他答案中看到。 为了平衡讨论,我将解释一个观点,即永远不应该写孤儿实例,我认为这是绝大多数经验丰富的Haskeller中的主要观点。 我自己的意见是在中间的某个地方,我会在最后解释。

这个问题源于这样一个事实:当同一个类和类型存在多个实例声明时,标准Haskell中没有机制指定要使用哪个实例声明。 相反,程序被编译器拒绝。

最简单的效果就是你可以有一个完美的工作程序,它会突然停止编译,因为别人在你的模块的一些远离依赖的情况下做出的改变。

更糟糕的是,由于远程更改,工作程序可能会在运行时崩溃 。 您可能正在使用您假设的方法来自某个实例声明,并且可能会默默地被另一个不同的实例替换,该实例只是不同程度地导致您的程序开始莫名其妙地崩溃。

希望保证这些问题不会发生在他们身上的人必须遵守这样的规则:如果任何人在任何地方曾经为某种类型声明某个类的实例,则在写入的任何程序中都不得再次声明其他实例由任何人。 当然,还有使用的解决办法newtype宣布一个新的实例,但总是至少一个小小的不便,有时一个主要的。 所以从这个意义上说,那些故意写孤儿实例的人是非常不礼貌的。

那么对这个问题应该做些什么呢? 反孤儿实例阵营说,GHC警告是一个错误,它需要是一个拒绝任何声明孤儿实例的尝试的错误。 同时,我们必须自律,不惜一切代价避免。

正如你所看到的,有些人并不担心这些潜在的问题。 他们实际上鼓励将孤儿实例用作分离问题的工具,正如你所建议的那样,并且应该说个人应该根据具体情况确保没有问题。 我被其他人的孤儿实例困扰了很多次,以确信这种态度太过于激进。

我认为正确的解决方案是为Haskell的导入机制添加一个扩展来控制实例的导入。 这并不能完全解决问题,但它会为保护我们的计划免受世界上已存在的孤儿事件的损害提供一些帮助。 然后,随着时间的推移,我可能会确信,在某些有限的情况下,也许孤儿实例可能并不那么糟糕。 (这种非常诱惑的原因是,反孤儿营地的一些人反对我的提议。)

我的结论是,至少目前来说,我强烈建议你避免宣布任何孤儿实例,如果没有其他原因,要体谅别人。 使用newtype


继续并压制此警告!

你身处优秀的公司。 Conal在“TypeCompose”中做到这一点。 “chp-mtl”和“chp-transformers”这样做,“control-monad-exception-mtl”和“control-monad-exception-monadsfd”等等。

顺便说一句,你可能已经知道这一点,但对于那些没有,并在搜索上绊倒你的问题:

{-# OPTIONS_GHC -fno-warn-orphans #-}

编辑:

我承认Yitz在答复中提到的问题是真正的问题。 不过,我认为不会将孤立实例作为一个问题来使用,并且我尝试挑选“至少所有的罪恶”,这是非常谨慎的使用孤立实例。

我在简短的回答中只用了一个感叹号,因为你的问题表明你已经很清楚这些问题。 否则,我会不那么热心:)

有点转移,但我相信,在完美的世界中完美的解决方案毫不妥协:

我相信Yitz提到的问题(不知道选择哪个实例)可以在“整体”编程系统中解决,其中:

  • 你不是简单地编辑纯粹的文本文件,而是由环境协助(例如代码完成只提示相关类型的东西等)
  • “低级”语言对类型类没有特别的支持,相反,函数表被明确地传递
  • 但是,“更高层次”的编程环境以类似于Haskell现在呈现方式(通常不会看到传递的函数表)的方式显示代码,并且当它们很明显时为您选择显式类型类(for例子所有Functor的情况只有一个选择),并且当有几个例子(压缩列表Applicative或列表monad Applicative,First / Last / lift可能是Monoid)时,它允许您选择使用哪个实例。
  • 在任何情况下,即使自动为您挑选实例,环境也可轻松让您查看使用哪个实例,并具有简单的界面(超链接或悬停界面等)
  • 从幻想世界(或希望未来)回来,现在:我建议尽量避免孤儿实例,同时在“真正需要”时仍然使用它们


    孤儿实例是一个令人讨厌的事情,但在我看来,它们有时是必需的。 我经常将图书馆的类型来自一个图书馆,而一个班级来自另一个图书馆。 当然,这些库的作者不能期望为类型和类的每种可能的组合提供实例。 所以我必须提供他们,所以他们是孤儿。

    当你需要提供一个实例时,你应该将这个类型包装成一个新类型的想法是一个有理论价值的想法,但在很多情况下它太乏味了; 这是那些没有编写Haskell代码的人提出的一种想法。 :)

    所以继续并提供孤立实例。 它们是无害的。
    如果你可以用孤立实例崩溃ghc,那么这是一个bug,应该这样报告。 (bug ghc有/没有检测到多个实例并不难解决。)

    但是请注意,将来有些时候别人可能会像现有一样添加某个实例,并且可能会收到(编译时)错误。

    链接地址: http://www.djcxy.com/p/5671.html

    上一篇: Orphaned instances in Haskell

    下一篇: What does the `forall` keyword in Haskell/GHC do?