Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 重叠实例有什么好的用例吗?_Haskell - Fatal编程技术网

Haskell 重叠实例有什么好的用例吗?

Haskell 重叠实例有什么好的用例吗?,haskell,Haskell,我正在设计一个库,它将从使用OverlappingInstances编译器标志中获益匪浅。但谈到了这一扩展,并警告其危险性。我的问题是,有没有在任何地方都能很好地使用这个扩展的例子?关于如何封装坏消息和正确使用扩展,有什么经验法则吗 大多数人请求重叠实例,因为他们想要约束导向的推理,而不是类型导向的推理。类型类是为类型定向推理而创建的,Haskell并没有为约束定向推理提供优雅的解决方案 但是,您仍然可以使用新类型“封装优点”。给定以下实例定义,该定义容易出现重叠实例: instance (So

我正在设计一个库,它将从使用OverlappingInstances编译器标志中获益匪浅。但谈到了这一扩展,并警告其危险性。我的问题是,有没有在任何地方都能很好地使用这个扩展的例子?关于如何封装坏消息和正确使用扩展,有什么经验法则吗

大多数人请求重叠实例,因为他们想要约束导向的推理,而不是类型导向的推理。类型类是为类型定向推理而创建的,Haskell并没有为约束定向推理提供优雅的解决方案

但是,您仍然可以使用新类型“封装优点”。给定以下实例定义,该定义容易出现重叠实例:

instance (SomeConstraint a) => SomeClass a where ...
您可以改为使用:

newtype N a = N { unN :: a }

instance (SomeConstraint a) => SomeClass (N a) where ...

现在Haskell的类型类系统有一个合适的特定类型来匹配(即
na
),而不是免费匹配每一个类型。这使您可以控制实例的范围,因为现在只有包装在
N
newtype中的内容才会匹配。

也许一个思维实验可以稍微解开这个扩展的神秘面纱

让我们假设我们已经放弃了使用多个模式案例定义的函数必须全部位于一个位置的限制,这样您就可以在模块顶部编写
foo(“bar”,Nothing)=…
,然后在其他地方编写类似
foo(“baz”,Just x)=…
。事实上,让我们更进一步,允许在不同的模块中完全定义案例

如果您认为这听起来会让人困惑,并且容易出错,那么您是对的

为了恢复理智的外表,我们可以增加一些限制。例如(ha,ha),我们可以要求持有以下财产:

  • 在任何使用此类函数的地方,给定的参数必须与一种模式完全匹配。其他任何东西都是编译器错误
  • 添加新模式(包括导入另一个模块)永远不会改变有效代码的含义——要么选择相同的模式,要么产生编译器错误
应该清楚的是,匹配像
True
Nothing
这样的简单构造函数非常简单。我们还可以节省一些东西,并假设编译器可以消除文本的歧义,比如上面的
“bar”
“baz”

另一方面,使用诸如
(x,只是y)
之类的模式绑定参数会变得很尴尬——编写这样的模式意味着放弃以后编写诸如
(True,)
(False,只是“foobar”)
之类的模式的能力,因为这样会产生歧义。更糟糕的是,模式守卫几乎毫无用处,因为他们需要非常普通的匹配。许多常见的习语会产生无尽的歧义,当然,编写一个“默认”的直通模式是完全不可能的

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

我们可以通过放松所需的属性来重新获得一些表达能力,例如:

  • 无论在何处使用这样的函数,它都必须至少匹配一种模式。没有匹配项是编译器错误
  • 如果使用函数使多个模式匹配,则将使用最特定的模式。如果没有唯一的最特定的模式,则会产生错误
  • 如果函数的使用方式与常规实例相匹配,但可以在运行时应用于与更特定实例相匹配的参数,则这是编译器错误
注意,我们现在的情况是,仅仅导入一个模块就可以通过引入一个新的、更具体的模式来改变函数的行为。在涉及高阶函数的复杂情况下,情况也可能变得模糊。不过,在许多情况下,问题不大可能出现——比如,在库中定义一个通用的故障模式,同时允许客户端代码在需要时添加特定的案例

这大概就是
重叠实例的位置。正如上面的例子中所建议的,如果创建新的重叠总是不可能的或需要的,并且不同的模块最终不会看到不同的、冲突的实例,那么它可能是好的


真正的原因是,
OverlappingInstances
所消除的限制使得使用类型类在“开放世界”的假设下表现合理,即以后可以添加任何可能的实例。通过放松这些要求,你自己承担了这个负担;因此,请仔细考虑添加新实例的所有方法,以及这些场景是否是一个重大问题。如果您确信在晦涩难懂的情况下没有什么能收支平衡,那么就继续使用扩展。

OverlappingInstances
允许您编写许多有用的东西,否则在typeclass级别无法实现,但绝大多数都可以重新组织为使用单个函数依赖项 (此处以实物多态形式书写)

这目前只能通过
OverlappingInstance
实现(以完全通用的方式)。用例的例子包括Oleg将OOP编码为Haskell。因此,我的一个很好地使用重叠实例的例子是

通过编译器支持(甚至在类型函数而不是fundep级别上工作),可以非常简单地提供这个特定的功能,因此在某个地方粘贴一个带有TypeEq的模块对我来说并不那么糟糕

当我从事危险的类型类黑客行为时,我经常发现
不连贯性
行为(选择第一个匹配实例)更容易推理,也更灵活,因此至少在设计的探索阶段使用它。一旦我有了我想要的东西,我就会尝试摆脱扩展,特别注意行为不太好的扩展
class TypeEq (a :: k) (b :: k) (t :: Bool) | a b -> t where
  typeEq :: Proxy a -> Proxy b -> HBool t