在学习GHC扩展时,我在Haskell学院遇到了RankNTypes
,它有以下示例:
main = print $ rankN (+1)
rankN :: (forall n. Num n => n -> n) -> (Int, Double)
rankN f = (f 1, f 1.0)
本文提供了rankN
的替代类型:
rankN :: forall n. Num n => (n -> n) -> (Int, Double)
对差异的解释是
后一个签名需要一个从n到n的函数,其中有一个Num n;前一个签名对于每个Numn需要从n到n的函数。
我可以理解前一种类型需要一个签名,它是圆括号中的签名或者更一般的签名。我不理解后一种签名只需要一个函数n -> n
来表示“some Num n
“的解释。有人能详细说明一下吗?你如何“阅读”前一种签名,使它听起来像它的意思?后一个签名是否与Num n => (n -> n) -> (Int, Double)
相同,而不需要forall
?
3条答案
按热度按时间kx5bkwkv1#
在通常的情况下(
forall n. Num n => (n -> n) -> (Int, Double)
),我们首先选择一个n
*,然后提供一个函数,这样我们就可以传入Int -> Int
、Double -> Double
、Rational -> Rational
等类型的函数。在秩为2的情况下(
(forall n. Num n => n -> n) -> (Int, Double)
),我们必须在知道n
之前提供函数,这意味着该函数必须对任何n
都有效;我为前面的示例列出的示例都不起作用。我们在给出的示例代码中需要这样做,因为传入的函数
f
应用于两种不同的类型:一个Int
和一个Double
。所以它必须对它们都有效。第一种情况是正常的,因为类型变量默认是这样工作的,如果你根本没有
forall
,你的类型签名就相当于在一开始就有了它(这被称为前束形式),所以Num n => (n -> n) -> (Int, Double)
隐式地和forall n. Num n => (n -> n) -> (Int, Double)
相同。对于 any
n
,函数的类型是什么?它就是forall n. Num n => n -> n
。js81xvg62#
你如何"读"这个以前的签名,使它听起来像它的意思?
你能读
如"rankN取参数
f :: Num n => n -> n
"并返回(Int, Double)
,其中f :: Num n => n -> n
可被读作"对于任何数值类型n
,f
可取n
并返回n
"。一级定义
将被读作"对于任何数值类型
n
,rank1
接受参数f :: n -> n
并返回(Int, Double)
"。后一个签名是否与
Num n => (n -> n) -> (Int, Double)
相同,而不需要forall
?是的,默认情况下,所有
forall
都隐式放置在最外面的位置(导致秩为1的类型)。6qqygrtg3#
在
rankN
的情况下,f
必须是对所有数值类型n
有效的多态函数。在
rank1
的情况下,f
只需为单个数值类型定义。下面是一些代码来说明这一点:
在评论中回应@helpwithhaskell的问题......
考虑以下函数:
也就是说,我们将
f
同时应用于Int和Double,如果不使用RankNTypes,它将不会执行类型检查:以下签名均不适用于???: