我是否正确理解带有fromIntegral的haskell代码?

我是否正确理解带有fromIntegral的haskell代码?,haskell,Haskell,我(想)我完全理解为什么6/(length[1,2,3])不起作用:length返回一个int(length::Foldable t=>ta->int),int不实现/函数 一个可行的建议解决方案是6/fromIntegral(长度[1,2,3]) 这个问题是关于为什么这个解决方案有效 我的猜测是:函数fromIntegral(请参见:)以多种方式实现(非显式,但仍然如此),因此它可以接收许多不同的类型并返回许多不同的类型。因此,在编译代码时,haskell将在可能的返回类型中进行选择,并使用适

我(想)我完全理解为什么
6/(length[1,2,3])
不起作用:length返回一个int(
length::Foldable t=>ta->int
),int不实现
/
函数

一个可行的建议解决方案是
6/fromIntegral(长度[1,2,3])

这个问题是关于为什么这个解决方案有效

我的猜测是:函数
fromIntegral
(请参见:)以多种方式实现(非显式,但仍然如此),因此它可以接收许多不同的类型并返回许多不同的类型。因此,在编译代码时,haskell将在可能的返回类型中进行选择,并使用适当的返回类型(在本例中为double或float)

我的猜测正确吗


任何填补这一解释空白的做法都会让人感激

你的直觉基本上是正确的

与任何在返回类型上具有多态性的函数一样,
fromIntegral
将生成程序进行类型检查所需的任何类型的值。粗略地说,它生成上下文所需的类型

例如,
fromIntegral someValue+(3::Double)
将使
fromIntegral
产生一个
Double
,因为这是所有类型检查所需要的


如果在类型推断之后,Haskell仍然不知道使用哪种特定类型,它通常会引发错误,抱怨歧义。作为一个主要的例外,当需要数字类型时,会使用一些“默认”类型,而不是引发错误。这个例外使得
print3
不需要编写
print(3::Integer)
或类似的东西就可以工作。

from Integral
具有type
(Integral a,Num b)=>a->b
,因此您可以获得实现所需
Num
的任何类型

(/)
具有类型
分数a=>a->a->a
,并且由于
分数
Num
的子类,因此来自integral的
将能够提供适当的类型


剩下的唯一一点是
6/..
实际返回的类型;表达式本身的类型为
分数a=>a
。这将取决于验证表达式的上下文。例如,在GHCi中,我相信它将默认为
Double

from integral
的类型为
。(积分a,数值b)=>a->b
。如果您熟悉其他语言,
对于所有AB.
就像
(Java,C#)或
模板
(C++):它为泛型函数引入了类型变量的作用域

通常,
forall
是隐式的,但您可以在GHCi中使用例如
:set-fprint explicit forall
来显示它,并在代码中使用扩展名
ExplicitForAll
来启用它。(请注意,
-fprint explicit foralls
将在类型变量周围放置大括号,这是无效的语法;我将在下面解释。)

类型参数
a
b
(以及约束
Integral a
Num b
)是调用
from Integral
传递给函数的参数,但与值级参数不同,这些类型级参数在编译时由编译器隐式传递。当你这样写的时候:

6.0 / fromIntegral (length [1, 2, 3]) :: Double
fromIntegral
被推断为具有类型参数
Int
(因为
length
返回
Int
)和
Double
(因为类型注释)。因此,类型变量的填写方式如下:

(Integral Int, Num Double) => Int -> Double
然后,编译器找到
实例整数Int
实例Num Double
的定义,并使用它们的
到integer::forall a的实现。整数a=>a>Integer
)和
fromInteger::forall a。Num a=>Integer->a
(专门用于从Integer::Integer->Double进行转换)。最后,这些调用GHC基元函数,如执行实际转换的
smallInteger
doubleFromInteger

使用
TypeApplications
扩展,您可以显式编写这些类型参数:

> :set -fprint-explicit-foralls

> :t fromIntegral -- original function
fromIntegral :: forall {a} {b}. (Integral a, Num b) => a -> b

> :t fromIntegral @Int -- applied to one type argument
fromIntegral @Int :: forall {b}. Num b => Int -> b

> :t fromIntegral @Int @Double -- applied to both type arguments
fromIntegral @Int @Double :: Int -> Double

> :t fromIntegral @Int @Double 5 -- both type arguments *and* value argument
fromIntegral @Int @Double 5 :: Double
TypeApplications
-fprint explicit forall
有时在
forall
量词中的类型变量周围打印花括号的原因:类型参数的顺序由类型签名定义,大括号表示有一个类型签名,指定可以用
TypeApplications
填充哪些类型参数。如果没有大括号,则没有签名,并且不能使用
TypeApplications
,因为没有指定参数顺序

使用
:type
/
:t
时,它将始终打印大括号,因为此命令推断表达式的类型;如果您想知道定义的签名,则需要使用
:type+v
/
:t+v
。例如:

> :set -XTypeApplications -fprint-explicit-foralls

> example1 x y = x

> example2 :: a -> b -> a; example2 x y = x

> :t example1 -- always prints braces
example1 :: forall {p1} {p2}. p1 -> p2 -> p1

> :t example2 -- always prints braces
example2 :: forall {a} {b}. a -> b -> a

> :t +v example1 -- definition has no signature; prints braces
example1 :: forall {p1} {p2}. p1 -> p2 -> p1

> :t +v example2 -- definition has a signature; no braces
example2 :: forall a b. a -> b -> a
您可以将
TypeApplications
example2
一起使用,但不能使用
example1

> :t example2 @Int @Char
example2 @Int @Char :: Int -> Char -> Int

> :t example1 @Int @Char

<interactive>:1:1: error:
    • Cannot apply expression of type ‘p10 -> p20 -> p10’
      to a visible type argument ‘Int’
    • In the expression: example1 @Int @Char
:t示例2@Int@Char
示例2@Int@Char::Int->Char->Int
>:t示例1@Int@Char
:1:1:错误:
•无法应用“p10->p20->p10”类型的表达式
指向可见类型参数“Int”
•在表达式中:example1@Int@Char

(/)
具有类型
浮点a=>a->a->a->a
,但
长度
返回
整数
,而
整数
不是
浮点
,使用
from integral
,可以将该
整数
转换为任何
数值
类型。
(/)整数
小数定义,然而,非
浮动
分数
本身不是
浮动