理解Haskell的秩N型

2nbm6dog  于 2023-01-26  发布在  其他
关注(0)|答案(3)|浏览(166)

在学习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

kx5bkwkv

kx5bkwkv1#

在通常的情况下(forall n. Num n => (n -> n) -> (Int, Double)),我们首先选择一个n *,然后提供一个函数,这样我们就可以传入Int -> IntDouble -> DoubleRational -> 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)相同。
对于 anyn,函数的类型是什么?它就是forall n. Num n => n -> n

js81xvg6

js81xvg62#

你如何"读"这个以前的签名,使它听起来像它的意思?
你能读

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

如"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的类型)。

6qqygrtg

6qqygrtg3#

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

相关问题