内容很大部分来自 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
。类型类实例有点像隐式转换,编译器会在上下文中去找这个东西,使得函数体中能够正确调用类型类约定的函数。值得注意的是和类型变量不同,类型类实例只能靠编译器推断,不能手动传进去。