第七章 Hindley-Milner 类型签名
最后更新于:2022-04-02 04:17:48
[TOC]
## Hindley-Milner 类型签名
- 类型(type)是让所有不同背景的人都能高效沟通的元语言
- 类型签名还衍生出了 “自由定理(free theorems)” 的概念
- 因为类型是可以推断的,所以明确的类型签名并不是必要的
- 类型签名不但可以用于编译时检测(compile time checks),还是最好的文档
## 类型签名实例
实例1
```
// match :: Regex -> (String -> [String])
var match = curry(function(reg, s){
return s.match(reg);
});
// onHoliday :: String -> [String]
var onHoliday = match(/holiday/ig);
```
- 函数都写成类似 a -> b 这个样子,其中 a 和b 是任意类型的变量
- 因此,capitalize 函数的类型签名可以理解为“一个接受 String 返回 String 的函数”
- 每传一个参数,就会弹出类型签名最前面的那个类型
实例2
```
// id :: a -> a
var id = function(x){ return x; }
```
- `a->a` 表示从任意类型转到任意类型,但是类型是同一类型
实例3
```
// map :: (a -> b) -> [a] -> [b]
var map = curry(function(f, xs){
return xs.map(f);
});
```
- b 可能与 a 类型相同,也可能不相同
- 签名说明:map 接受两个参数,第一个是**函数**(从任意类型 a 到任意类型 b 的函数);第二个是一个**数组**,元素是任意类型的 a;map 最后返回的是一个类型 b 的**数组**
## 缩小可能性范围
一旦引入一个类型变量,就会出现一个奇怪的特性叫做*parametricity*,。这个特性表明,函数将会*以一种统一的行为作用于所有的类型*
```
// reverse :: [a] -> [a]
```
意看`head`,可以看到它接受`[a]`返回`a`。我们除了知道参数是个`数组`,其他的一概不知;所以函数的功能就只限于操作这个数组上
类型`a`的多态性(polymorphism)都会大幅缩小`reverse`函数可能的行为的范围
## 自由定理
```
// head :: [a] -> a
compose(f, head) == compose(head, map(f));
// filter :: (a -> Bool) -> [a] -> [a]
compose(map(f), filter(compose(p, f))) == compose(filter(p), map(f));
```
第一个例子中,等式左边说的是,先获取数组的`头部`(译者注:即第一个元素),然后对它调用函数`f`;等式右边说的是,先对数组中的每一个元素调用`f`,然后再取其返回结果的`头部`。这两个表达式的作用是相等的,但是前者要快得多
## 类型约束
签名也可以把类型约束为一个特定的接口(interface)
```
// sort :: Ord a => [a] -> [a]
```
胖箭头左边表明的是这样一个事实:a 一定是个 Ord 对象。也就是说,a 必须要实现 Ord 接口。Ord 到底是什么?它是从哪来的?在一门强类型语言中,它可能就是一个自定义的接口,能够让不同的值排序
';