Haskell-为什么我们要对单类型输出使用RankNTypes?
我尝试了以下代码:Haskell-为什么我们要对单类型输出使用RankNTypes?,haskell,Haskell,我尝试了以下代码: printing = print $ rank2 (+1) rank2 :: Num n => (n -> n) -> Double rank2 f = f 1.0 它抛出了错误: Couldn't match expected type ‘Double’ with actual type ‘n’ ‘n’ is a rigid type variable bound by the type signature 如果我将Double更改为Int,错误也会从
printing = print $ rank2 (+1)
rank2 :: Num n => (n -> n) -> Double
rank2 f = f 1.0
它抛出了错误:
Couldn't match expected type ‘Double’ with actual type ‘n’
‘n’ is a rigid type variable bound by the type signature
如果我将Double
更改为Int
,错误也会从'Double'更改为'Int'
仅当我使用RankNTypes
GHC语言扩展时,此错误才得以解决:
{-# LANGUAGE RankNTypes #-}
printing = print $ rank2 (+1)
rank2 :: (forall n. Num n => n -> n) -> Double
rank2 f = f 1.0
我已在以下网址阅读答案:
但是我不太明白为什么当我不使用元组作为输出时会出现这个错误,比如forall n。Num n=>(n->n)->(Int,Double)
,其中元组是(Int,Double)
。如果元组是输出,函数f
在选择类型Int
或Double
时可能会遇到冲突。但在本例中,我使用的是单一类型的输出
这是因为函数f
是严格多态的吗?如果是这样,这难道不意味着多态性是毫无意义的吗?该函数无法使自身适应双精度
或Int
。因此您需要
rank1 :: Num n => (n -> n) -> Double
rank1 f = f 1.0
如果它进行了类型检查,则必须能够使用它,例如,如下所示:
bad :: Double
bad = rank1 (\n -> n + (sqrt (-1) :: Complex Double))
但这显然是行不通的,因为现在你把一个虚数加到一个实数上,结果应该仍然是一个实数双数
与
您可以阻止这种用法,因为作为参数传递的函数必须能够用于任意数字类型,因此我不可能在混合中添加具体的复杂双精度
,只能使用泛型Num
操作:
allright :: Double
allright = rank2 (\n -> n + abs (-1))
所以你想要
rank1 :: Num n => (n -> n) -> Double
rank1 f = f 1.0
如果它进行了类型检查,则必须能够使用它,例如,如下所示:
bad :: Double
bad = rank1 (\n -> n + (sqrt (-1) :: Complex Double))
但这显然是行不通的,因为现在你把一个虚数加到一个实数上,结果应该仍然是一个实数双数
与
您可以阻止这种用法,因为作为参数传递的函数必须能够用于任意数字类型,因此我不可能在混合中添加具体的复杂双精度
,只能使用泛型Num
操作:
allright :: Double
allright = rank2 (\n -> n + abs (-1))
这个定义,
rank2 :: Num n => (n -> n) -> Double
rank2 f = f 1.0
不起作用,因为Haskell中的标准rank-1多态性表示调用者可以选择类型变量的含义。使用类型为Int->Int
的参数f
调用该类型的函数是有效的。这不适用于该定义,因为f
需要接受并返回一个Double
当您将其级别提高时,您可以通过要求f
在所有n
上具有多态性来解决这一问题,而不是处理调用方选择的某种类型n
。现在可以在body中的typeDouble->Double
处使用f
,并获得type检查的结果
在这种情况下,这种更高级别的类型并不是完全无用的-可以防止传入的函数在Num
类型类之外执行任何操作。例如,它可以加法、乘法、使用文字和其他一些东西,但不能除法或使用trig函数
有时,对输入功能的这种限制非常重要,因此较高级别的类型提供了价值。是一个使用更高级别类型来防止输入值做会破坏Haskell的评估模型并导致错误程序的事情的示例
但我不确定在这种情况下你是否真的会关心这种事情。最简单的方法可能是使用排名1的类型,它可以更直接地支持您正在做的事情。这个定义
rank2 :: Num n => (n -> n) -> Double
rank2 f = f 1.0
不起作用,因为Haskell中的标准rank-1多态性表示调用者可以选择类型变量的含义。使用类型为Int->Int
的参数f
调用该类型的函数是有效的。这不适用于该定义,因为f
需要接受并返回一个Double
当您将其级别提高时,您可以通过要求f
在所有n
上具有多态性来解决这一问题,而不是处理调用方选择的某种类型n
。现在可以在body中的typeDouble->Double
处使用f
,并获得type检查的结果
在这种情况下,这种更高级别的类型并不是完全无用的-可以防止传入的函数在Num
类型类之外执行任何操作。例如,它可以加法、乘法、使用文字和其他一些东西,但不能除法或使用trig函数
有时,对输入功能的这种限制非常重要,因此较高级别的类型提供了价值。是一个使用更高级别类型来防止输入值做会破坏Haskell的评估模型并导致错误程序的事情的示例
但我不确定在这种情况下你是否真的会关心这种事情。可能最简单的方法是使用排名1的类型,它可以更直接地支持您正在做的事情。这:
rank1 :: Num n => (n -> n) -> Double
是这个的缩写:
rank1 :: forall n. Num n => (n -> n) -> Double
您可以使用{-#Language ExplicitForAll}
在类型签名的顶层为all编写一个显式。当然,RankNTypes
也允许这样做,但这是一种排名1的类型
这是具有以下参数的多态函数类型:
- 一种类型
n
,由类型推断隐式提供或显式地由{-#Language TypeApplications}
提供
- 该类型
n
的Num
实例,也由编译器隐式提供;及
- 从
n
到n
的函数(f
)
rank1
的调用者指定每个参数的参数值
在rank1
的定义中,它可以调用f
,但是