Haskell重叠/非相干实例

Cli*_*ton 12 haskell types

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

{-# 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]) 
Run Code Online (Sandbox Code Playgroud)

C. *_*ann 15

这真的很简单.让我们问一下GHCi的类型isList2是什么:

?x. x ? :t isList2
isList2 :: a -> Bool
Run Code Online (Sandbox Code Playgroud)

这与[a]实例不匹配(即使它可以通过统一),但它确实匹配a实例.因此,GHC选择a实例,所以isList2返回False.

这种行为恰恰IncoherentInstances意味着什么.实际上,这是一个相当不错的演示.


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

?x. x ? :t isList2
isList2 :: [Integer] -> Bool
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为它isList2是一个未使用函数语法定义的顶级绑定,因此受到Dreaded Monomorphism Restriction的限制.所以它专门用于它实际使用的实例.

添加NoMonomorphismRestriction和禁用IncoherentInstances,我们得到这个:

?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'
Run Code Online (Sandbox Code Playgroud)

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


关于问题的编辑,我不相信没有类型注释就可以得到期望的结果.

第一个选项是提供isList2类型签名,以防止IncoherentInstances过早选择实例.

isList2 :: (IsList a) => a -> Bool
isList2 = isList
Run Code Online (Sandbox Code Playgroud)

您可能需要在isList未提及参数的任何其他地方(甚至间接地)执行相同的操作.

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

main = 
  print (isList (42 :: Integer)) >> 
  print (isList2 (42 :: Integer)) >> 
  print (isList [42]) >> 
  print (isList2 [42]) 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,有足够的信息来选择一个最具体的实例,它也是如此OverlappingInstances.