我是Haskell的新手,我正在做一些练习来尽可能多地学习类型,但有些问题对我来说真的很困惑。我正在努力的练习如下:
以下表达式有哪些类型?如果表达式没有类型,请尽可能多地声明。也 请务必在需要时声明必要的类限制。
5 + 8 :: ?
(+) 2 :: ?
(+2) :: ?
(2+) :: ?
我知道 5 + 8 将返回一个 Int,但其他表达式不是它们自己的有效表达式。这是否意味着它们没有类型,或者我应该将它们视为函数(f :: Int -> Int)(f x = x + 2)?
首先,答案:
5 + 8
具有可以专门用于Int
的forall a. Num a => a
类型。(+) 2
和(2 +)
是相同的,并且都具有可以专门用于Int -> Int
的forall a. Num a => a -> a
类型。(+ 2)
是不同的,但也具有可以专门用于Int -> Int
的forall a. Num a => a -> a
类型。(+)
具有可以专门用于Int -> Int -> Int
的forall a. Num a => a -> a -> a
类型。
有关进一步说明,请继续阅读。
一个文字,多种类型
在 Haskell 中,像114514
这样的数字文字没有像Int
这样的具体类型。这很好,因为我们有许多不同类型的数字,包括。Int
、Integer
、Float
、Double
等等,我们不希望每种类型都有不同的符号。
文字5
、114514
和1919810
都具有以下类型
forall a. Num a => a
你可以这样读:"对于任何类型a
,如果a
是Num
类型类的实例,那么该值可以具有a
类型。Int
、Integer
、Float
和Double
都是Num
的实例,并且因为Haskell具有(相对)强的类型推断, 在不同的上下文中,它将专门针对具体类型,例如Int
.
那么什么是类型类呢?
类型类
我们在Haskell中表达某些类型"支持"某些操作的方式是通过类型类。类型类是一组函数签名,没有真正的实现。它们代表我们想要对某些类型进行的操作(例如Num
表示我们想要对数值类型进行的操作),但对不同类型的实际实现可能会有所不同(整数和浮点数的实际计算确实不同)。
我们可以使类型成为类型类的instance
(请注意,这与面向对象编程中的实例和类无关),通过实际为该类型定义这些函数。通过这种方式,我们定义了此类型来支持这些操作。
Num
是类型类之一,表示对数值运算的支持。它部分定义如下(为了减少冗长,我没有在这里放完整的):
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
你可以看到,在签名中,我们有a
而不是真正的具体类型,这些函数被称为多态的,也就是说,它们是泛型的,可以专门用于不同的类型。
例如,如果a
和b
都是Int
s,那么a + b
也有类型Int
,因为 haskell 推断我们在这里使用的+
应该是为Int
定义的,因为它的两个参数都是Int
s。
因此,如果某个类型是Num
的实例,则意味着为该类型定义了+
、-
和*
运算符。作为Num
的实例意味着支持这些运营商。
部分
Haskell的一个好处是它(相对)灵活的中缀运算符。在 Haskell 中,中缀运算符只不过是带有中缀符号(这只是一种语法糖)的普通函数。我们还可以以前缀方式使用中缀运算符。例如,5 + 8
等效于(+) 5 8
。
看来你对(+) 5
、(+5)
和(5+)
感到困惑。请记住,如果我们在中缀函数的两侧都加上括号,我们就会将其设为前缀。而且你可能已经知道,前缀函数可以部分应用,也就是说,只给函数一些参数,所以它变成了一个参数较少的函数,以后要给出。(+) 5
意味着我们通过只给出它的第一个参数来部分应用(+)
,所以它变成了一个等待另一个参数的函数,或者最初是它的第二个参数。我们可以将(+) 5
应用于8
,使其变为((+) 5) 8
,这相当于5 + 8
。
另一方面,(5+)
和(+5)
称为节,这是中缀运算符的另一种语法糖。(5+)
意味着您填充了运算符的左侧,它变成了等待其右侧的功能。(5+) 8
表示5 + 8
或(+) 5 8
。(+5)
是翻转的,即你填充了右侧,所以它是一个等待左侧的功能。(+5) 8
表示8 + 5
或(+) 8 5
。
Haskell是一种与你可能学过的其他语言非常不同的语言;每个编译的表达式都有一个类型。函数是一流的,每个函数也有一个类型。类型思维将真正有助于您的学习进度。
在哈斯克尔:
- 运算符只是函数
- 函数可以提供比声明更少的参数
- 括号可用于消除参数的顺序和组件的歧义
综合起来,这意味着:
- (+) 只是 + 函数,并且还需要 2 个参数
- (1 +) 是提供第一个参数的函数,将再取一个参数
- (+ 2) 是另一个提供参数的函数,但这次是第二个。它还等待另一个论点