OverlappingInstances有什么好的用例吗?

Mik*_*cki 16 haskell

我正在设计一个可以从使用OverlappingInstances编译器标志中获益的库.但是,每个人都谈论这个扩展及其危险的警告.我的问题是,是否存在在hackage上任何地方使用此扩展的示例?关于如何封装不良并正确使用扩展,是否有任何经验法则?

C. *_*ann 31

也许一个思想实验会使这个扩展神秘化.

让我们假设我们已经放弃了限制,使用多个模式案例定义的函数必须全部在一个地方,这样你就可以foo ("bar", Nothing) = ...在模块的顶部写入,然后像foo ("baz", Just x) = ...其他地方一样.事实上,让我们更进一步,允许案例完全不同的模块中定义!

如果您认为这听起来很混乱且容易出错,那么您就是对的.

为了恢复一些理智,我们可以添加一些限制.例如(ha,ha),我们可能需要以下属性来保存:

  • 在使用这样的函数的任何地方,给出的参数必须恰好匹配一个模式.其他任何东西都是编译器错误.
  • 添加新模式(包括通过导入另一个模块)永远不应该改变有效代码的含义 - 选择相同的模式,或者产生编译器错误.

应该清楚的是,匹配简单的构造函数是简单的TrueNothing简单的.我们也可以handwave事情了一下,假设编译器可以消除歧义的文字,喜欢"bar""baz"上面.

另一方面,使用类似模式的绑定参数(x, Just y)变得笨拙 - 编写这样的模式意味着放弃编写类似(True, _)(False, Just "foobar")稍后模式的能力,因为这会产生歧义.更糟糕的是,模式守卫几乎无用,因为他们需要非常一般的比赛.许多常见的习语会产生无穷无尽的模糊性问题,当然,编写"默认"的落后模式是完全不可能的.

这大致是类型类实例的情况.

我们可以通过放松所需的属性来重新获得一些表现力:

  • 在使用这样的函数的任何地方,它必须匹配至少一个模式.没有匹配是编译器错误.
  • 如果使用函数使得多个模式匹配,则将使用最特定的模式.如果没有唯一的最特定模式,则会产生错误.
  • 如果函数以与通用实例匹配的方式使用,但可以在运行时应用于与更具体的实例匹配的参数,则这是编译器错误.

请注意,我们现在处于这样一种情况:仅导入模块可以通过引入一个新的,更具体的模式来改变函数的行为.在涉及高阶函数的复杂情况下,事情可能会变得模糊.尽管如此,在许多情况下,问题不太可能发生 - 例如,在库中定义通用的直通模式,同时让客户端代码在需要时添加特定的案例.

这大概就是OverlappingInstances你的地方.正如上面的例子所示,如果创建新的重叠总是不可能或不可取的,并且不同的模块最终不会看到不同的,冲突的实例,那么它可能就好了.

真正归结的是,在OverlappingInstances"开放世界"的假设下,使用类型类所取得的限制可以使得任何可能的实例都可以在以后添加.通过放宽这些要求,你自己承担了这个负担; 因此,请考虑可以添加新实例的所有方式以及这些方案中是否存在任何重大问题.如果你确信在晦涩和狡猾的角落情况下什么都不会破裂,那么继续使用扩展.


Gab*_*lez 13

大多数人请求重叠实例,因为他们需要约束导向推理而不是类型导向推理.类型定向推理的类型类和Haskell不能为约束定向推理提供优雅的解决方案.

但是,您仍然可以通过使用newtypes"封装善意".鉴于以下实例定义容易出现重叠实例:

instance (SomeConstraint a) => SomeClass a where ...
Run Code Online (Sandbox Code Playgroud)

你可以改用:

newtype N a = N { unN :: a }

instance (SomeConstraint a) => SomeClass (N a) where ...
Run Code Online (Sandbox Code Playgroud)

现在Haskell的类型系统有一个适当的特定类型匹配(即N a),而不是无偿地匹配每一种类型.这使您可以控制实例的范围,因为现在只有Nnewtype中包含的内容才会匹配.

  • 是否存在缺乏约束导向推理的理论基础? (3认同)
  • 这是我读过这个建议最明确和最简洁的方式.它应该放在所有的教程中. (3认同)