模式中的类型推断

时间:2014-11-09 14:59:17

标签: haskell ghc

我注意到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的类型是函数的输出类型?

2 个答案:

答案 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的类型。也许编译器不够聪明,无法解决这个问题。