我是否正确理解带有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
,可以将该整数转换为任何数值类型。(/)整数由小数定义,然而,非浮动分数本身不是浮动或