使用GADT进行类型推断 - a0是不可触及的

时间:2015-02-18 11:14:39

标签: haskell gadt

假设我有这个程序

{-# LANGUAGE GADTs #-}

data My a where
  A :: Int  -> My Int
  B :: Char -> My Char


main :: IO ()
main = do
  let x = undefined :: My a

  case x of
    A v -> print v

  -- print x

编译好。

但是当我在print x发表评论时,我得到了:

gadt.hs: line 13, column 12:
  Couldn't match type ‘a0’ with ‘()’
    ‘a0’ is untouchable
      inside the constraints (a1 ~ GHC.Types.Int)
      bound by a pattern with constructor
                 Main.A :: GHC.Types.Int -> Main.My GHC.Types.Int,
               in a case alternative
      at /home/niklas/src/hs/gadt-binary.hs:13:5-7
  Expected type: GHC.Types.IO a0
    Actual type: GHC.Types.IO ()
  In the expression: System.IO.print v
  In a case alternative: Main.A v -> System.IO.print v

为什么我在第13行(A v -> print v)而不是仅在print x行中出现此错误?

我认为第一次出现应该确定类型。

请赐教:)

1 个答案:

答案 0 :(得分:21)

首先请注意,这与特定print x无关:在结束main时,您会收到相同的错误。 putStrLn "done"

所以问题确实存在于case块中,即只需要do的最后一个语句具有do块签名的类型。其他陈述只需要在同一个monad中,即IO a0而不是IO ()

现在,通常从语句本身推断出a0,所以例如你可以写

do getLine
   putStrLn "discarded input"

虽然getLine :: IO String而不是IO ()。但是,在您的示例中,信息print :: ... -> IO ()来自case块内,来自GADT匹配。这样的GADT匹配与其他Haskell语句的行为不同:基本上,它们不允许任何类型信息逃避其范围,因为如果信息来自GADT构造函数,那么它在case之外是不正确的。

在那个特定的例子中,似乎很明显a0 ~ ()与GADT比赛中的a1 ~ Int没有任何关系,但总的来说,这样的事实只有在GHC追踪时才能证明用于所有类型信息来自哪里。我不知道这是否可能,它肯定会比Haskell的Hindley-Milner系统更复杂,后者严重依赖于统一类型信息,这基本上假设信息的位置并不重要来自。

因此,GADT匹配只是作为一个刚性的“类型信息二极管”:内部的东西永远不能用于确定外部的类型,就像整个case块应该是{{1} }。

但是,您可以使用相当丑陋的

手动断言
IO ()

或写作

  (case x of
     A v -> print v
    ) :: IO ()