考虑以下代码:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Foo where
class Foo a
class SomeClass a
instance {-# OVERLAPPABLE #-} (Foo a) => SomeClass a
bar :: (SomeClass a) => a -> Int
bar = const 0
foo :: (SomeClass a) => a -> Int
foo t = let x = bar t in x
此处,foo
调用bar
,并且应该能够在其上下文中使用SomeClass
约束来执行此操作。相反,GHC假定它必须使用Foo a => SomeClass a
实例:
Foo.hs:16:17:
Could not deduce (Foo a) arising from a use of ‘bar’
from the context (SomeClass a)
bound by the type signature for foo :: SomeClass a => a -> Int
at Foo.hs:15:8-32
Possible fix:
add (Foo a) to the context of
the inferred type of x :: Int
or the type signature for foo :: SomeClass a => a -> Int
In the expression: bar t
In an equation for ‘x’: x = bar t
In the expression: let x = bar t in x
有两种方法可以解决这个问题:
foo
中,将let x = bar t in x
更改为bar t
instance SomeClass Int
附加到我的程序这里发生了什么?为什么会出现此问题,为什么这些修复工作正常?
这个例子当然是由我的实际问题简化的。我在Cubix多语言转换框架工作期间遇到过这个问题(arxiv.org/pdf/1707.04600)。
我的实际问题涉及具有约束InjF f IdentL FunctionExpL
的函数(&#34; f是一种语言,其中标识符可用于表示函数调用中的函数&#34;)。我有一个(可重叠的)实例(FunctionIdent :<: f) => InjF f IdentL FunctionExpL
,这是typechecker抓住的,给我一个虚假的Could not deduce FunctionIdent :<: f
错误。
即使在特定InjF Foo IdentL FunctionExpL
的范围内存在Foo
实例,此错误仍然存在,因此左下方的答案预测其他实例应该解决问题,不是全文。
答案 0 :(得分:3)
原因是编译器尝试使x
尽可能通用:它希望它是(SomeClass a) => Int
(注意如果你自己写出来,这将是一个模糊的类型)。防止这种奇怪的本地类型的一种方法是启用-XMonoLocalBinds
,但我不会真的推荐它。
现在,编译器继续进行类型检查。那时,范围内只有一个instance SomeClass
,即全能(Foo a) => SomeClass a
,所以没有歧义。 (原则上,Haskell完全禁止实例解析中的歧义; OVERLAPPABLE
颠覆了这一点,但只有在需要时才会跳转到操作,就像你有一个额外的instance SomeClass Int
一样。)因此,编译器立即提交到该实例,因此消除(SomeClass a)
c
的类型检查。如果您确实希望将类约束替换为实例的约束,则必须执行此操作。在给定的示例中,这似乎无用,但是当您拥有表单实例时,它实际上是至关重要的
instance Bar a => SomeClass (Maybe a)
...这比你的代码更合理,因为可以表示为超类约束,并且完全可以没有任何重叠。如果编译器在这些情况下没有将约束降低到Bar a
,那么它实际上永远无法解析Maybe
类型。
结论:避免重叠实例;如果可能的话,通过超类声明来表达类关系,否则将约束重新化为GADT(这很笨拙,但可以精确控制将使用哪个约束)。