Haskell:这些表达式的类型是什么(如果有的话)?

Nif*_*dde 1 haskell functional-programming

我是 Haskell 的新手,我正在做一些练习来尽可能多地学习关于类型的知识,但有些问题对我来说真的很困惑。我正在苦苦挣扎的练习内容如下:

以下表达式的类型是什么?如果表达式没有类型,请尽可能多地说明。还要确保在需要时声明必要的类限制。

5 + 8 :: ?
(+) 2 :: ?
(+2) :: ?
(2+) :: ?
Run Code Online (Sandbox Code Playgroud)

我知道 5 + 8 将返回一个 Int,但其他人本身并不是有效的表达式。这是否意味着它们没有类型,还是我应该将它们视为函数 (f :: Int -> Int)(fx = x + 2)?

day*_*ily 5

首先,答案:

  • 5 + 8具有forall a. Num a => a可以专用于的类型Int
  • (+) 2(2 +)是相同的,并且都具有forall a. Num a => a -> a可以专用于的类型Int -> Int
  • (+ 2)是不同的,但也有forall a. Num a => a -> a可以专门化的类型Int -> Int
  • (+)具有forall a. Num a => a -> a -> a可以专用于的类型Int -> Int -> Int

如需进一步说明,请继续阅读。


一种文字,多种类型

在Haskell,像数字文字114514不能有一个具体的类型一样Int。这很好,因为我们有许多不同类型的数字,包括。Int, Integer, Float,Double等等,我们不希望每种类型都有不同的符号。

文字5, 114514, 和1919810都具有类型

forall a. Num a => a
Run Code Online (Sandbox Code Playgroud)

你可以这样读:“对于任何类型a,如果aNum typeclass 的一个实例,那么值可以有 type a。” Int, Integer,FloatDouble都是 , 的实例Num,并且因为 Haskell 具有(相对)强类型推断,在不同的上下文中它将专门用于具体类型,如Int.

那么什么是类型类呢?

类型类

我们在 Haskell 中表达某种类型“支持”某些操作的方式是通过typeclasses。类型类是一组函数签名,没有真正的实现。它们代表我们要对某些类型进行的Num操作(例如代表我们要对数字类型进行的操作),但不同类型的实际实现可能有所不同(整数和浮点数的实际计算确实不同)。

我们可以通过为该类型实际定义这些函数来使类型成为instance类型类的an (注意,这与面向对象编程中的实例和类无关)。这样,我们定义了这个类型来支持这些操作。

Num是类型类之一,表示支持数字运算。它的部分定义如下(为了减少冗长,我没有把完整的放在这里):

class Num a where
    (+) :: a -> a -> a
    (-) :: a -> a -> a
    (*) :: a -> a -> a
Run Code Online (Sandbox Code Playgroud)

你可以看到在签名中我们有as 而不是真正的具体类型,这些函数被称为是多态的,也就是说,它们是通用的,可以专门用于不同的类型。

例如,如果ab都是Ints,那么a + b也有类型Int,因为 Haskell 推断+我们这里使用的 应该是为 定义的Int那个,因为它的两个参数都是Ints。

因此,如果某个类型是 的实例Num,则意味着为该类型定义了+, -and*运算符。作为Num支持这些运营商的手段的一个实例。


部分

Haskell 的一个好处是它(相对)灵活的中缀运算符。在 Haskell 中,中缀运算符只不过是带有中缀符号(这只是一种语法糖)的普通函数。我们还可以以前缀方式使用中缀运算符。例如,5 + 8相当于(+) 5 8

您似乎对(+) 5,(+5)和感到困惑(5+)。请记住,如果我们将括号放在中缀函数的两侧,我们将其设为前缀。而且你可能已经知道,前缀函数可以部分应用,即只给函数一些参数,这样就变成了一个参数较少的函数,以后再给。(+) 5意味着我们部分应用(+)只给它的第一个参数,所以它成为另一个的功能等待一个参数,或者原本其第二个参数。我们可以应用(+) 58所以它变成((+) 5) 8,它等价于5 + 8

另一方面,(5+)(+5)被称为部分,这是中缀运算符的另一种语法糖。(5+)意味着您填充了运算符的左侧,并且它成为等待其右侧的函数。(5+) 8意味着5 + 8(+) 5 8(+5)被翻转,即你填充了右手边,所以它是一个等待左手边的函数。(+5) 8意味着8 + 5(+) 8 5


Haskell 是一种与你可能学过的其他语言非常不同的语言。编译的每个表达式都有一个类型。函数是一流的,每个函数也有一个类型。按类型思考将真正有助于您的学习进度。