假设我有这个程序
{-# 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
行中出现此错误?
我认为第一次出现应该确定类型。
请赐教:)
答案 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 ()