Haskell中的孤立实例 [英] Orphaned instances in Haskell

查看:103
本文介绍了Haskell中的孤立实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  Publisher.hs:45:9:
警告:orphan实例:实例ToSElem结果

类型类 ToSElem 不是我的,它由 HStringTemplate



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



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



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

所以,我要找的是一些验证,我的想法是合理的,而且我会有理由忽略/抑制这个警告,或者一个更有说服力的论点来反对我做事的方式。

我明白你为什么要这样做,但不幸的是,Haskell类似乎可以像你说的那样开放,这可能只是一种错觉。许多人认为这样做的可能性是Haskell规范中的一个错误,因为我将在下面解释。无论如何,如果它实际上不适合实例,则需要在声明类的模块中或在声明类型的模块中声明,这可能表示您应该使用 newtype 或其他类型的包装。



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



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



最简单的效果就是你可以有一个完美的工作程序,因为别人的改变而突然停止编译

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



希望保证这些问题不会发生在他们身上的人必须遵守以下规则:如果任何人在任何地方曾经为某种类型声明过某个类的实例,则不得有其他实例在任何人编写的程序中再次声明。当然,使用 newtype 来声明一个新实例是个解决方法,但这总是起码造成一点小麻烦,有时也是一个很大的麻烦。
所以在这个意义上说,那些故意写孤儿实例的人是相当不礼貌的。
所以应该怎么解决这个问题呢?反孤儿实例阵营说,GHC警告是一个错误,它需要是一个拒绝任何声明孤儿实例的尝试的错误。与此同时,我们必须自律,不惜一切代价避免它们。

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



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

我的结论是,至少对于目前,我强烈建议您避免宣布任何孤儿实例,如果没有其他原因,请考虑其他人。使用 newtype


When compiling my Haskell application with the -Wall option, GHC complains about orphaned instances, for example:

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

The type class ToSElem is not mine, it's defined by HStringTemplate.

Now I know how to fix this (move the instance declaration into the module where Result is declared), and I know why GHC would prefer to avoid orphaned instances, but I still believe that my way is better. I don't care if the compiler is inconvenienced - rather it than me.

The reason I want to declare my ToSElem instances in the Publisher module is because it is the Publisher module that depends on HStringTemplate, not the other modules. I am trying to maintain a separation of concerns and avoid having every module depend on HStringTemplate.

I thought that one of the advantages of Haskell's type classes, when compared for example to Java's interfaces, is that they are open rather than closed and therefore the instances do not have to be declared in the same place as the data type. GHC's advice seems to be to ignore this.

So, what I'm looking for is either some validation that my thinking is sound and that I would be justified in ignoring/suppressing this warning, or a more convincing argument against doing things my way.

解决方案

I understand why you want to do this, but unfortunately, it may be only an illusion that Haskell classes seem to be "open" in the way that you say. Many people feel that the possibility of doing this is a bug in the Haskell specification, for reasons I'll explain below. Anyway, if it is really not appropriate for the instance you need to be declared either in the module where the class is declared or in the module where the type is declared, that is probably a sign that you should be using a newtype or some other wrapper around your type.

The reasons why orphan instances need to be avoided run far deeper than convenience of the compiler. This topic is rather controversial, as you can see from other answers. To balance the discussion, I am going to explain the point of view that one should never, ever, write orphan instances, which I think is the majority opinion among experienced Haskellers. My own opinion is somewhere in the middle, which I'll explain at the end.

The problem stems from the fact that when more than one instance declaration exists for the same class and type, there is no mechanism in standard Haskell to specify which to use. Rather, the program is rejected by the compiler.

The simplest effect of that is that you could have a perfectly working program that would suddenly stop compiling because of a change someone else makes in some far off dependency of your module.

Even worse, it's possible for a working program to start crashing at runtime because of a distant change. You could be using a method that you are assuming comes from a certain instance declaration, and it could silently be replaced by a different instance that is just different enough to cause your program to start inexplicably crashing.

People who want guarantees that these problems won't ever happen to them must follow the rule that if anyone, anywhere, has ever declared an instance of a certain class for a certain type, no other instance must ever be declared again in any program written by anyone. Of course, there is the workaround of using a newtype to declare a new instance, but that is always at least a minor inconvenience, and sometimes a major one. So in this sense, those who write orphan instances intentionally are being rather impolite.

So what should be done about this problem? The anti-orphan-instance camp says that the GHC warning is a bug, it needs to be an error that rejects any attempt to declare an orphan instance. In the meantime, we must exercise self-discipline and avoid them at all costs.

As you have seen, there are those who are not so worried about those potential problems. They actually encourage the use of orphan instances as a tool for separation of concerns, as you suggest, and say that one should just make sure on a case-by-case basis that there is no problem. I have been inconvenienced enough times by other people's orphan instances to be convinced that this attitude is too cavalier.

I think the right solution would be to add an extension to Haskell's import mechanism that would control the import of instances. That would not solve the problems completely, but it would give some help towards protecting our programs against damage from the orphan instances that already exist in the world. And then, with time, I might become convinced that in certain limited cases, perhaps an orphan instance might not be so bad. (And that very temptation is the reason that some in the anti-orphan-instance camp are opposed to my proposal.)

My conclusion from all this is that at least for the time being, I would strongly advise that you avoid declaring any orphan instances, to be considerate to others if for no other reason. Use a newtype.

这篇关于Haskell中的孤立实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆