let如何与Haskell中较高级别的类型交互?
我遇到了一个令人困惑的情况,那是一个级别更高的类型。我知道如何使它工作,但我不明白工作版本和非工作版本之间的区别 根据这些背景定义:let如何与Haskell中较高级别的类型交互?,haskell,let,higher-rank-types,impredicativetypes,Haskell,Let,Higher Rank Types,Impredicativetypes,我遇到了一个令人困惑的情况,那是一个级别更高的类型。我知道如何使它工作,但我不明白工作版本和非工作版本之间的区别 根据这些背景定义: {-# LANGUAGE RankNTypes #-} data AugmentedRational = Exact Integer Rational -- Exact z q is q * pi^z | Approximate (forall a.Floating a => a) approximateVa
{-# LANGUAGE RankNTypes #-}
data AugmentedRational = Exact Integer Rational -- Exact z q is q * pi^z
| Approximate (forall a.Floating a => a)
approximateValue :: Floating a => AugmentedRational -> a
approximateValue (Exact z q) = (pi ** (fromInteger z)) * (fromRational q)
approximateValue (Approximate x) = x
。。。这两种功能的区别是什么
版本A(我最初写的东西不起作用)
导致:
Cannot instantiate unification variable `b0'
with a type involving foralls: forall a. Floating a => a
Perhaps you want ImpredicativeTypes
In the first argument of `(.)', namely `Approximate'
In the expression: Approximate . f . approximateValue
如果您遵循非指示性类型建议(我不完全理解),则错误消息将更改为:
No instance for (Floating (forall a. Floating a => a))
arising from a use of `f'
In the first argument of `(.)', namely `f'
In the second argument of `(.)', namely `f . approximateValue'
In the expression: Approximate . f . approximateValue
版本B,哪个有效
其他非工作版本
总结
这是怎么回事?在我看来,这5种定义在句法上都是表达完全相同事物的不同方式
编辑说明:删除了有关类型是存在类型的错误声明。(您的问题中没有使用存在类型。您拥有的是具有多态参数的构造函数Approximate
,导致Approximate
具有秩-2类型,并导致更高秩类型和类型推断的问题。)
简单的回答是:无点样式和更高级别的类型不能很好地结合在一起。如果涉及多态参数,请避免使用函数组合,并坚持使用普通函数应用程序或$
,一切都会很好。以一种被接受的方式编写近似值的直接方法是:
approx :: (forall a . Floating a => a -> a) -> AugmentedRational -> AugmentedRational
approx f ar = Approximate (f (approximateValue ar))
问题是GHC不能正确支持“非指示性”类型。这意味着:如果一个函数是多态的,它的类型变量可以用单态类型实例化,但不能用本身是多态的类型实例化。为什么这与这里有关
让我们看看你写的:
approx :: (forall a.Floating a => a -> a) -> AugmentedRational -> AugmentedRational
approx f = Approximate . f . approximateValue
您在此处使用函数组合(
)两次。函数组合的类型如下:
(.) :: (b -> c) -> (a -> b) -> a -> c
infixr 9 .
因此,上面的定义被解析为
Approximate . (f . approximateValue)
但是
具有秩2类型。因此,将近似值的类型与(.)
的第一个参数匹配意味着:
b = forall a. Floating a => a
c = AugmentedRational
必须坚持下去
GHC不允许将b
实例化为多态类型。
它建议将ImpredicativeTypes
作为一种可能使其工作的语言扩展,但不幸的是,它是一种非常脆弱的语言扩展,通常不鼓励使用它。正如您所看到的,即使启用了ImpredicativeTypes
,GHC通常仍需要相当多的额外类型注释,因此如果没有额外的更改,r程序将无法工作
普通函数应用程序内置于GHC中,并在类型检查期间以不同的方式处理。这就是为什么更直接地定义近似值
有效的原因。使用$
也可以,但这只是因为GHC中实现了一种特殊的hack,告诉类型检查器$
与函数应用程序没有什么不同。近似(对于所有a.浮点a=>a)
不是一个存在类型,而是一个通用类型。封装在该构造函数中的值必须可用于所有浮点类型,而不仅仅是一个。例如近似(2.3::Double)
是一个错误。由于@chi所写的,这实际上不是一个非常有用的数据类型。您可能最好只存储一个Rational
或一个Double
之类的东西。我认为它实际上做了Rational不会做的事情。也就是说,它允许您使用pi::将一个注入器浮动到任何一个变量中(与Rational中的one一起),并获得底层表示的精度。因此,假设a是128位浮点类型,所有计算近似值的函数都将以该精度执行,而不是以Double
或硬编码的任何有理近似值执行。再进一步玩一下,我注意到我认为BGGY的行为,或者至少我不理解。我在这里报道过:
(.) :: (b -> c) -> (a -> b) -> a -> c
infixr 9 .
Approximate . (f . approximateValue)
Approximate :: (forall a. Floating a => a) -> AugmentedRational
b = forall a. Floating a => a
c = AugmentedRational