我刚才在Haskell打一些代码高尔夫球,我遇到了一个当时对我来说没有多大意义的错误。决定在GHCi中检查一下,现在我真的很困惑。
λ> :t replicate <$> readLn
replicate <$> readLn :: IO (a -> [a])
λ> f <- replicate <$> readLn
-- I type 4 and press Enter
λ> :t f
f :: GHC.Types.Any -> [GHC.Types.Any]
为什么f
不属于a -> [a]
类型?当然,我可以unsafeCoerce
,但这冗长而可怕。
IO (a -> [a])
是一种多态类型。扩展,这意味着forall a. IO (a -> [a])
.现在,有两件事在这里不起作用。第一,这是一种产生单态函数的多态IO
作用。实质上,此操作的每次执行都会为一种类型生成一个函数。a -> [a]
实际上不是一个有效的类型,但是如果你的意思是你想要一个forall a. a -> [a]
,你不会得到一个:
main = do
f <- replicate <$> readLn
print (f (5 :: Int)) -- f can be *one of* Int -> [Int] or Float -> [Float], but not both
print (f (5 :: Float)) -- doesn't compile, comment either line out and it will
第二,GHC不支持非谓词多态性。从理论上讲,如果你正确地编写了IO
动作,你可以让它IO (forall a. a -> [a])
,但GHC不支持将多态类型(如forall a. a -> [a]
(放入容器中,如IO
。
在您的情况下,由于您不使用f
,GHC 不知道它应该以哪种类型实例化操作,但它必须选择一个,因此它默认为Any
。
编辑:绕过"无谓词类型"限制的传统方法是将它们隐藏在newtypes
中:
{-# LANGUAGE RankNTypes #-}
-- interestingly, this is a numeric type (it represents the natural numbers)
newtype Replicator = Replicator { runReplicator :: forall a. a -> [a] }
mkReplicator :: Int -> Replicator
mkReplicator i = Replicator (replicate i)
-- mkReplicator =# replicate
main = do
Replicator f <- mkReplicator <$> readLn
print (f (5 :: Int))
print (f (5 :: Float)) -- should work now
可能不值得...
这里有两个问题。一个由以下代码说明:
Prelude> do { f <- return (replicate 4); print (f 'a'); print (f 'b') }
"aaaa"
"bbbb"
Prelude> do { f <- return (replicate 4); print (f 'a'); print (f "b") }
[...]
* Couldn't match expected type `Char' with actual type `[Char]'
[...]
Haskell类型系统的局限性导致f
是单态的,也就是说,只能在一种类型(您选择的(上使用。HTNW已经涵盖了这一点。
第二个问题是GHCi特有的。它由以下代码说明:
Prelude> do { f <- return (replicate 4); print (f 'a') }
"aaaa"
Prelude> f <- return (replicate 4)
Prelude> print (f 'a')
[...]
* Couldn't match expected type `GHC.Types.Any'
with actual type `Char'
[...]
GHC 有两种方法可以将类型分配给类型不明确的表达式。一个是数字默认值,这在这里无关紧要。当类型(数字或其他(没有类型类约束时,另一种情况适用。在这种情况下,类型不会影响程序的运行时行为,因此选择什么并不重要。全康使用GHC.Types.Any
.
通常,您永远不会看到第二种默认的结果,因为它仅在编译器知道类型无关紧要之后才会发生。
但是,GHCi 会在您键入的每一行之后应用这些默认规则,让您没有机会用以后的代码来约束类型。因此,您可以获得您想要的任何类型,而不是您想要的任何类型,只要它是Any
。