为什么选择全康.类型,这里有吗?



我刚才在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

相关内容

  • 没有找到相关文章

最新更新