[英]Understanding Haskell's RankNTypes
在通过 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)
差异的解释是
对于某些 Num n,后一个签名需要从 n 到 n 的函数; 前一个签名要求每个 Num n 都有一个从 n 到 n 的函数。
我可以理解,前一种类型需要一个签名来作为括号中的内容或更一般的内容。 我不明白后一个签名只需要一个函数n -> n
来表示“some Num n
”的解释。 有人可以详细说明和澄清吗? 你如何“阅读”这个以前的签名,使它听起来像它的意思? 后一个签名是否与简单的Num n => (n -> n) -> (Int, Double)
而无需forall
?
在正常情况下( forall n. Num n => (n -> n) -> (Int, Double)
),我们选择一个n
第一和然后提供的功能。 所以我们可以传入Int -> Int
、 Double -> Double
、 Rational -> Rational
等类型的函数。
在 Rank 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)
隐含相同forall n. Num n => (n -> n) -> (Int, Double)
forall n. Num n => (n -> n) -> (Int, Double)
。
适用于任何n
的函数类型是什么? 这正是forall n. Num n => n -> n
forall n. Num n => n -> n
。
你如何“阅读”这个以前的签名,使它听起来像它的意思?
你可以阅读
rankN :: (forall n. Num n => n -> n) -> (Int, Double)
因为“rankN 接受一个参数f :: Num n => n -> n
”并返回(Int, Double)
,其中f :: Num n => n -> n
可以读作“对于任何数字类型n
, f
可以取一个n
并返回一个n
"。
一级定义
rank1 :: forall n. Num n => (n -> n) -> (Int, Double)
然后将被读作“对于任何数字类型n
, rank1
接受一个参数f :: n -> n
并返回一个(Int, Double)
”。
后一个签名是否与简单的
Num n => (n -> n) -> (Int, Double)
而无需forall
?
是的,默认情况下,所有forall
都隐式地放置在最外面的位置(导致 rank-1 类型)。
在rankN
情况下, f
必须是一个对所有数字类型n
都有效的多态函数。
在rank1
情况下, f
只需为单个数字类型定义。
这是一些说明这一点的代码:
{-# LANGUAGE RankNTypes #-}
rankN :: (forall n. Num n => n -> n) -> (Int, Double)
rankN = undefined
rank1 :: forall n. Num n => (n -> n) -> (Int, Double)
rank1 = undefined
foo :: Int -> Int -- monomorphic
foo n = n + 1
test1 = rank1 foo -- OK
test2 = rankN foo -- does not type check
test3 = rankN (+1) -- OK since (+1) is polymorphic
更新
回应@helpwithhaskell 在评论中提出的问题...
考虑这个函数:
bar :: (forall n. Num n => n -> n) -> (Int, Double) -> (Int, Double)
bar f (i,d) = (f i, f d)
也就是说,我们将f
应用于 Int 和 Double。 不使用 RankNTypes 就不会进行类型检查:
-- doesn't work
bar' :: ??? -> (Int, Double) -> (Int, Double)
bar' f (i,d) = (f i, f d)
以下签名均不适用于 ???:
Num n => (n -> n)
Int -> Int
Double -> Double
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.