内容很大部分来自 https://haskell.love/richard-eisenberg/ 这一视频,奇怪的知识增加了。
引入
1 | const :: a -> b -> a |
const 函数接收几个参数呢?是 a 类型的 x 与 b 类型的 y —— 两个吗?
为了便捷,GHC 默认会省略掉 rank 1 的 quantifier。我们可以打开语言扩展ExplicitForAll 并在 GHCi中开启参数 -fprint-explicit-foralls,以便在代码中可以显式书写量词,以及使得在 GHCi 使用 :t 时会同时打印出量词。当然,有了 UnicodeSyntax 语言扩展,∀ 也能被 parse。所以我们可以改写一下新的 const:
1 | const :: ∀ a b . a -> b -> a |
这样就清晰多了 —— const 接收四个参数:类型变量 a 和 b,以及 x 和 y。有的同志(用过 exteff 相关库)可能知道一个叫 TypeApplications 的扩展,开启它之后类型变量也能显式指定,例如 const @Int @String 233 "QAQ"。这样以来我们可以更加确信 const 有四个参数。
再来看一个相似的例子:
1 | shout :: ∀ a . Show a => a -> String |
shout 接收三个参数:类型变量 a、类型类实例 Show a 以及 x。类型类实例有点像隐式转换,编译器会在上下文中去找这个东西,使得函数体中能够正确调用类型类约定的函数。值得注意的是和类型变量不同,类型类实例只能靠编译器推断,不能手动传进去。