我注意到GHC想要一种类型签名,我认为应该推断出来。我把我的例子最小化到了这个,几乎肯定没有任何意义(我不建议在你最喜欢的类型上运行它):
{-# LANGUAGE GADTs, RankNTypes, ScopedTypeVariables,
TypeOperators, NoMonomorphismRestriction #-}
module Foo where
import Data.Typeable
data Bar rp rq
data Foo b = Foo (Foo b)
rebar :: forall rp rq rp' rp'' . (Typeable rp', Typeable rp'')
=> Proxy rp' -> Proxy rp'' -> Foo rp -> Foo (Bar rp rq)
rebar p1 p2 (Foo x) =
-- The signature for y should be inferred...
let y = rebar p1 p2 x -- :: Foo (Bar rp rq)
-- The case statement has nothing to do with the type of y
in case (eqT :: Maybe (rp' :~: rp'')) of
Just Refl -> y
如果y
的定义没有类型签名,我会收到错误:
Foo.hs:19:20:
Couldn't match type ‘rq0’ with ‘rq’
‘rq0’ is untouchable
inside the constraints (rp' ~ rp'')
bound by a pattern with constructor
Refl :: forall (k :: BOX) (a1 :: k). a1 :~: a1,
in a case alternative
at testsuite/Foo.hs:19:12-15
‘rq’ is a rigid type variable bound by
the type signature for
rebar :: (Typeable rp', Typeable rp'') =>
Proxy rp' -> Proxy rp'' -> Foo rp -> Foo (Bar rp rq)
at testsuite/Foo.hs:12:20
Expected type: Foo (Bar rp rq)
Actual type: Foo (Bar rp rq0)
Relevant bindings include
y :: Foo (Bar rp rq0) (bound at testsuite/Foo.hs:16:7)
rebar :: Proxy rp' -> Proxy rp'' -> Foo rp -> Foo (Bar rp rq)
(bound at testsuite/Foo.hs:14:1)
In the expression: y
In a case alternative: Just Refl -> y
Failed, modules loaded: none.
由于多次出现可怕的单态限制,我开启了NoMonomorphismRestriction
,但这并未改变行为。
为什么不推断y
的类型是函数的输出类型?
答案 0 :(得分:7)
单态限制仅适用于顶级绑定。编译器知道y
的实际类型,但没有办法推断它的单形类型;这是类型错误的原因。如果你真的想关闭单态let绑定,你必须使用正确的扩展名:
{-# LANGUAGE NoMonoLocalBinds #-}
有了它,您的代码就会编译。
关于单态let绑定的更多细节,see the ghc wiki。
答案 1 :(得分:1)
我不熟悉GHC的打字算法。不过,我在猜测为什么编译器无法解决这个问题。
考虑以下代码:
rebar :: forall rp rq rp' rp'' . (Typeable rp', Typeable rp'')
=> Proxy rp' -> Proxy rp'' -> Foo rp -> Foo (Bar rp rq)
rebar p1 p2 (Foo x) =
let y = ... :: Foo (Bar rp Char)
in case (eqT :: Maybe (Char :~: rq)) of
Just Refl -> y
这应该编译,因为匹配Refl
证明Char ~ rq
,因此最后y
具有正确的返回类型Foo (Bar rp rq)
。该程序通过了类型检查。
但是,假设我们改为
let y = ... :: Foo (Bar rp rq)
在这种情况下,y
已经是正确的类型,而Refl
是无用的。同样,程序通过了类型检查。
现在,假设我们有 no 类型注释。如何编译弄清楚哪个是
let y = ...
绑定的正确类型?毕竟,(至少)有两个领先
正确输入整个rebar
。
这也可以解释为什么如果添加_ -> y
它确实有效:在这种情况下,编译器知道不需要Refl
。相反,如果您添加y -> error ""
,则无法推断出有关y
的信息。
实际的完整故事可能比上述更复杂:在这里,我方便地忽略来自y
定义的信息,即rebar p1 p2 x
。换句话说,我只考虑上下文对y
定义的约束,而不考虑那些转向另一个方向的约束。
在你的例子中,类型方程实际上是rp' ~ rp''
,这似乎与w.r.t无关。最后y
的类型。也许编译器不够聪明,无法解决这个问题。