为什么 GHC.Types.Any 在这里?

Why GHC.Types.Any here?

我刚才在 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 动作。本质上,每次执行此操作都会产生一个 one 类型的函数。 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.

编辑:绕过 "no impredicative types" 限制的传统方法是将它们隐藏到 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 使用 GHC.Types.Any.

通常情况下,您永远不会看到第二种默认设置的结果,因为它只会在编译器知道类型无关紧要之后发生。

但是,

GHCi 会在您输入的每一行后应用这些默认规则,让您没有机会用以后的代码来限制类型。因此,只要它是 Any.

,您就可以获得您想要的任何类型,而不是您想要的任何类型