繁体   English   中英

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

[英]Type inference with GADTs - a0 is untouchable

假设我有这个程序

{-# 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行中收到此错误?

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

请赐教:)

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

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

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

do getLine
   putStrLn "discarded input"

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

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

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

但是,您可以手动断言,相当丑陋

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

或通过写作

  () <- case x of
          A v -> print v

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM