fromIntegral 的类型为 forall a b. (Integral a, Num b) => a -> b。如果您熟悉其他语言,forall a b. 类似于<A, B>(Java,C#)或template<typename A, typename B>(C++):它为泛型函数引入了类型变量的作用域。
通常forall 是隐含的,但您可以使用例如:set -fprint-explicit-foralls 在 GHCi 中并在您的代码中使用 ExplicitForAll 扩展名启用它。 (注意-fprint-explicit-foralls 会在类型变量周围加上花括号,这是无效的语法;我将在下面解释。)
类型参数a 和b(以及约束Integral a 和Num b)是由调用者传入函数的参数 > of fromIntegral,但与值级参数不同,这些类型级参数由编译器在编译时隐式传递。当你写这样的东西时:
6.0 / fromIntegral (length [1, 2, 3]) :: Double
fromIntegral 被推断为具有类型参数Int(因为length 返回Int)和Double(因为类型注释)。所以类型变量是这样填写的:
(Integral Int, Num Double) => Int -> Double
然后编译器找到instance Integral Int 和instance Num Double 的定义,并使用它们的toInteger :: forall a. Integral a => a > Integer) 和fromInteger :: forall a. Num a => Integer -> a(专用于fromInteger :: 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-foralls 有时在 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