繁体   English   中英

理解 Haskell 的 RankNTypes

[英]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 -> IntDouble -> DoubleRational -> Rational等类型的函数。

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

我们需要在给出的示例代码中使用它,因为传入的函数f应用于两种不同的类型: IntDouble 所以它必须对他们两个都有效。

第一种情况是正常的,因为默认情况下类型变量就是这样工作的。 如果您根本没有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)

然后将被读作“对于任何数字类型nrank1接受一个参数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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM