Haskell重叠/不连贯实例 [英] Haskell Overlapping/Incoherent Instances

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

问题描述

我知道这是代码有点傻,但有人可以解释为什么 isList [42] 返回 True isList2 [42] 打印 False ,以及如何防止这种情况?我想更好地理解一些比较模糊的GHC类型的扩展,我认为这将是一个有趣的例子。

  { - #LANGUAGE FlexibleInstances# - } 
{ - #LANGUAGE OverlappingInstances# - }
{ - #LANGUAGE IncoherentInstances# - }
$ b $ class IsList a where
isList :: a - > Bool

实例IsList a其中
isList x = False

实例IsList [a]其中
isList x = True

isList2 = isList

main =
print(isList 42)>>
print(isList2 42)>>
print(isList [42])>>
print(isList2 [42])


解决方案

这真的很简单。让我们问GHCi isList2 的类型是:

 ∀x。 x⊢:t isList2 
isList2 :: a - > Bool

这不符合 [a] a 实例。因此,GHC选择 a 实例,所以 isList2 返回 False

这种行为恰恰是 IncoherentInstances 的意思。实际上,这是一个相当不错的演示。






如果您只是禁用 IncoherentInstances ,我们得到完全相反的效果,GHCi现在这样说:

 ∀x。 x⊢:t isList2 
isList2 :: [Integer] - > Bool

发生这种情况是因为 isList2 是顶部没有使用函数语法定义的级别绑定,因此受到可怕的单态限制。因此它专用于实际使用的实例。



添加 NoMonomorphismRestriction 以及禁用 IncoherentInstances ,我们得到它:

 ∀x。 x⊢:t isList2 
isList2 :: IsList a => a - > Bool
∀x。 x⊢isList2'a'
False
∀x。 x⊢isList2a
True
∀x。 x⊢isList2 undefined

< interactive>:19:1:
使用'isList2'产生的IsList a0的重叠实例

这是预期的重叠行为,如果选择不明确,则根据使用情况和投诉选择实例。






关于对问题的编辑,我不相信如果没有类型注释,所需的结果是不可能的。



第一个选项是给> isList2 一个类型签名,它可以防止 IncoherentInstances 过早选择一个实例。 p>

  isList2 ::(IsList a)=> a  - > Bool 
isList2 = isList

您可能需要在其他地方执行相同操作 isList 在没有应用于参数的情况下被提及(甚至是间接的)。

第二个选项是消除数字文字的歧义,禁用 IncoherentInstances

  main = 
print(isList(42 :: Integer))>>
print(isList2(42 :: Integer))>>
print(isList [42])>>
print(isList2 [42])

在这种情况下,有足够的信息可以选择最具体的实例,所以 OverlappingInstances 完成它的工作。


I know this is code is a bit silly, but can someone explain why this isList [42] returns True whereas isList2 [42] prints False, and how to prevent this? I'd like to get better understanding of some of the more obscure GHC type extensions, and I thought this would be an interesting example to figure out.

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE IncoherentInstances #-}

class IsList a where
  isList :: a -> Bool

instance IsList a where
  isList x = False

instance IsList [a] where
  isList x = True

isList2 = isList

main = 
  print (isList 42) >> 
  print (isList2 42) >> 
  print (isList [42]) >> 
  print (isList2 [42]) 

解决方案

It's really quite simple. Let's ask GHCi what the type of isList2 is:

∀x. x ⊢ :t isList2
isList2 :: a -> Bool

This doesn't match the [a] instance (even though it could, via unification), but it does match the a instance immediately. Therefore, GHC selects the a instance, so isList2 returns False.

This behavior is precisely what IncoherentInstances means. Actually, this is a rather nice demonstration of it.


Hilariously, if you simply disable IncoherentInstances, we get exactly the opposite effect, and GHCi now says this:

∀x. x ⊢ :t isList2
isList2 :: [Integer] -> Bool

This happens because isList2 is a top-level binding not defined using function syntax, and thus subject to the Dreaded Monomorphism Restriction. So it gets specialized to the instance it's actually used with.

Adding NoMonomorphismRestriction as well as disabling IncoherentInstances, we get this instead:

∀x. x ⊢ :t isList2
isList2 :: IsList a => a -> Bool
∀x. x ⊢ isList2 'a'
False
∀x. x ⊢ isList2 "a"
True
∀x. x ⊢ isList2 undefined

<interactive>:19:1:
    Overlapping instances for IsList a0 arising from a use of `isList2'

Which is the expected overlapping behavior, with the instance chosen based on use and complaints if the choice is ambiguous.


Regarding the edit to the question, I don't believe the desired result is possible without type annotations.

The first option is to give isList2 a type signature, which prevents IncoherentInstances from selecting an instance too early.

isList2 :: (IsList a) => a -> Bool
isList2 = isList

You'll probably need to do the same anywhere else isList is mentioned (even indirectly) without being applied to an argument.

The second option is to disambiguate the numeric literals and disable IncoherentInstances.

main = 
  print (isList (42 :: Integer)) >> 
  print (isList2 (42 :: Integer)) >> 
  print (isList [42]) >> 
  print (isList2 [42]) 

In this case, there's enough information to pick a most-specific instance, so OverlappingInstances does its thing.

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

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