了解 `5 1` 的类型

Understanding type of `5 1`

我在这里使用了这个 Haskell 解释器:https://tryhaskell.org/

当我提供输入 5 1 时,它告诉我 - 我假设它是类型 - 表达式的类型是 (Num a, Num (a -> t)) => t:

λ 5 1
:: (Num a, Num (a -> t)) => t

现在我试图理解如何解释这个,这就是我想到的。

a 是任何 Num 类型,a -> t 也是。该表达式产生某种类型 t,它是通过在理论上将 1(类型 a)应用于 5(类型 a -> t)而产生的。

这真的让我很不安,因为我不明白约束 Num (a -> t) 有何意义。从理论上讲,它看起来是正确的解释,但我找不到证明这一点的证据。


解释正确

这是有道理的,因为 Num 是一种多态类型 - 可以是一个函数。

你的解释是正确的。

I don't see how the constraint Num (a -> t) makes sense.

这里的关键是任何东西都可以是 Num 类型,只要在代码中的某处有它的实例声明。因此,如果对于某些 ataNum 类型),您写了 instance Num (a -> t) where ...,那么 a -> t 将是 Num 类型和 5 1 实际上可以计算为一个值。

数字在 Haskell 中是多态的。当你写类似

的东西时
5

编译器把它变成fromInteger (5 :: Integer) :: Num a => a。编译器事先并不知道要使用什么实例,所以它最好从上下文中猜测。如果你有 5 1,那么第一个数字是多态的 Num 类型,它也必须是一个函数。第二个数字刚好被看成是Num,所以这是很正常的。

但你可能会问 "How can a number be a function?" 如果世界上的一切都有意义,那么数字就不会是函数,但你实际上可以为它们编写一个或多或少表现的实例:

{-# LANGUAGE FlexibleInstances #-}

instance Num a => Num (a -> a) where
    fromInteger a = const (fromInteger a)
    a + b = \c -> a c + b c
    a * b = \c -> a c * b c
    abs a = \c -> abs (a c)
    signum a = \c -> signum (a c)
    negate a = \c -> negate (a c)

这满足定义,但它可能不是很有用。例如:

> let x = 1 :: Int -> Int; y = 2 :: Int -> Int
> x + y $ 0
3
> x + y $ 102089
3
> x + y $ undefined
3

所以在这里,参数与表达式无关,甚至不计算。来一个更有趣的怎么样:

> let x = (+10); y = (*10)
> x + y $ 0    -- x 0 + y 0 = 0 + 10 + 0 * 10
10
> x + y $ 1    -- x 1 + y 1 = 1 + 10 + 1 * 10
21
> x + y $ 2
32

等等。我敢肯定有人可以为它找到一个有趣的用例,但我不会推荐它,它显然不是很直观。

在Haskell中,数字是多态的:

λ> :t 5
5 :: Num a => a

这里重要要注意的是Haskell中的函数应用是空白。因此,要进行类似 5 2 类型检查的操作,您所要做的就是为 (a -> a) 类型创建一个实例。