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