了解Haskell的RankNTypes

时间:2015-10-31 00:28:16

标签: haskell types

在通过GHC扩展工作的同时,我遇到了RankNTypes at the School of Haskell,其中有以下示例:

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;前一个签名要求每个Num n都有一个从n到n的函数。"

我可以理解前一种类型需要签名是括号中的内容或更一般的内容。我不理解后一个签名只需要一个函数n -> n来解释"某些Num n"。有人可以详细阐述和澄清吗?你如何阅读"这个以前的签名让它听起来像是什么意思?后一个签名是否与简单Num n => (n -> n) -> (Int, Double)相同而不需要forall

3 个答案:

答案 0 :(得分:8)

在正常情况下(forall n. Num n => (n -> n) -> (Int, Double)),我们首先选择n ,然后提供功能。所以我们可以传递Int -> IntDouble -> DoubleRational -> Rational类型的函数,等等。

在Rank 2案例((forall n. Num n => n -> n) -> (Int, Double))中,我们必须提供之前的函数我们知道n。这意味着该函数必须适用于任何 n;我在上一个例子中列出的所有例子都不起作用。

我们需要为给出的示例代码提供此代码,因为传入的函数f适用于两种不同的类型:IntDouble。所以它必须适用于他们两个。

第一种情况是正常的,因为默认情况下类型变量是如何工作的。如果您根本没有forall,那么您的类型签名就相当于一开始就拥有它。 (这称为prenex表单。)因此Num n => (n -> n) -> (Int, Double)隐含与forall n. Num n => (n -> n) -> (Int, Double)相同。

适用于任何 n的功能的类型是什么?它完全是forall n. Num n => n -> n

答案 1 :(得分:3)

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

答案 2 :(得分:2)

  

你如何“阅读”这个以前的签名,这听起来像它的意思?

你可以阅读

rankN :: (forall n. Num n => n -> n) -> (Int, Double)

as“rankN采用参数f :: Num n => n -> n”并返回(Int, Double),其中f :: Num n => n -> n可以读作“对于任何数字类型nf可以取n并返回n“。

排名第一的定义

rank1 :: forall n. Num n => (n -> n) -> (Int, Double)

将被理解为“对于任何数字类型nrank1接受参数f :: n -> n并返回(Int, Double)”。

  

后一个签名是否与简单Num n => (n -> n) -> (Int, Double)相同而不需要forall

是的,默认情况下,所有forall都隐式放在最外面的位置(导致排名为1的类型)。