了解Haskell的RankNTypes
在通过GHC扩展工作的过程中,我遇到了哈斯克尔学院的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;前一个签名需要从n到n的函数,用于每个Num n。”
我可以理解,前一种类型需要签名才能成为括号内或更一般的内容。 我不明白后面的签名是否需要函数n -> n
来表示“一些Num n
”的解释。 有人可以详细说明吗? 你如何“读”这个前签名,使其听起来像是什么意思? 是后者的签名相同,只是Num n => (n -> n) -> (Int, Double)
,而不需要forall
?
在正常情况下(所有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
可言,你的类型的签名相当于在一开始就拥有它。 (这被称为prenex形式。)所以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
s的隐式地放置在最外面的位置(导致秩-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,它不会输入check:
-- doesn't work
bar' :: ??? -> (Int, Double) -> (Int, Double)
bar' f (i,d) = (f i, f d)
???以下签名都不适用于:
Num n => (n -> n)
Int -> Int
Double -> Double
链接地址: http://www.djcxy.com/p/7529.html