GHCi 忽略类型签名

GHCi ignores type signature

Prelude> let myprint = putStrLn . show
Prelude> :t myprint
myprint :: () -> IO ()

好的,这里没什么特别的。只是 GHCi 类型默认规则,我猜...

Prelude> let myprint = (putStrLn . show) :: Show x => x -> IO ()
Prelude> :t myprint
myprint :: () -> IO ()

这是什么魔法??你直截了当地无视我的类型声明?! O_O

有什么方法可以说服 GHCi 按照我的实际意图去做吗?

我们可以执行以下操作,单态限制为:

>let myprint :: Show x => x -> IO (); myprint = putStrLn . show
>:t myprint
myprint :: Show x => x -> IO ()

这与 let myprint = putStrLn . show :: Show x => x -> IO () 不同。在前一种情况下,我们有一个带有类型签名的绑定,在后一种情况下,我们有一个 let 绑定,在右侧有一个类型注释。单态性检查顶级类型签名,但不检查本地注释。

表达式添加类型注释,如

e :: type

使编译器检查 e 是否具有 type,并使用 type 来驱动类型变量实例化和实例选择。 但是,如果 type 是多态的,它仍然可以在以后实例化。考虑例如

(id :: a -> a) "hello"

上面,a 将被实例化为 String,尽管我有注释。此外,

foo :: Int -> Int
foo = (id :: a -> a)

将使 a 稍后实例化为 Int。上面的 id 注释没有给 GHC 任何信息:它已经知道 id 有那个类型。 我们可以删除它而不影响类型检查。也就是说,表达式 idid :: a->a 不仅动态等价,而且静态等价。

类似地,表达式

putStrLn . show

(putStrLn . show) :: Show x => x -> IO ()

是静态等价的:我们只是用 GHC 可以推断的类型来注释代码。换句话说,我们不会向 GHC 提供它不知道的任何信息。

注解类型检查后,GHC 可以进一步实例化 x。单态限制在您的示例中执行此操作。为防止这种情况,请为您要引入的 binding 使用注释,而不是为 expression:

myprint :: Show x => x -> IO ()
myprint = (putStrLn . show)