类型签名Hindley-Milner | 函数式编程

辨别类型和它们的含义是一项重要的技能,这项技能可以让你在函数式编程的路上走得更远。不仅论文、博客和文档等更易理解,类型签名本身也基本上能够告诉你它的函数性(functionality)。

背景: 在使用 ramdajs 这个函数工具库时每个函数下面都有一行类型签名,据说类型签名包含大量信息,阅读类型签名我们可以获知函数的传参与返回类型等等,没学它之前我是不信的,今天看来它和 ts 一样“真香”,今天来捋一下类型签名,看它到底香不香。

类型签名基本知识

// head :: [T] -> T
复制代码

上面便是一个类型签名, head 为函数名, T 为类型, -> 为处理过程, 最后一个T 为返回参数类型, 通过阅读类型签名可以把它翻译为typescript如下:

function head<T>(a: Array<T>):T;
复制代码

Hindley-Milner里面的类型具有多态性(polymorphism),类似ts里面的泛型(Generics)比如类型T即可以为Number, 也可以为String等等基本类型。

如果参数是函数时,类型签名这样写:

//  map :: (a -> b) -> [a] -> [b]
复制代码

上面的类型签名理解如下:

  1. 第一个参数为函数(a->b),函数接收类型为a的参数, 里面的-> 可以理解为处理过程,返回类型为b。
  2. 中间的-> [a], 接收第二个参数[a]: 数组类型为a, 函数式编程的世界,所以的函数都被柯里化了,所以中间的 -> x都理解为接收参数x.
  3. 最后的 -> [b]为返回值, 返回类型为b的数组。

typescript改写如下:

function map<a, b>(fn: (item:a) => b, list: Array<a>):Array<b>;
复制代码

ramdajs例子

看完这些实用例子,加深你对类型签名的理解,要还不明白可以过来打我。

pluck

Functor f => k → f {k: v} → f v
复制代码

这个例子里面, 我们可以接触到类型约束, 写法如下: 约束名 变量 =>

Functor f =>
复制代码

类型约束类似于typescript里的接口,上面的f变量受Functor的约束,点击文档可知f只能为数组或者对象。 类型签名解释:输入k类型,输入 {x: {k: v}} 或者 [{k: v}], 输出: {x: v} 或者 [v]

composeWith

((* → *), [(y → z), (x → y), …, (o → p), ((a, b, …, n) → o)]) → ((a, b, …, n) → z)
复制代码

这个例子够复杂吧,我就解释一下它,看能不能把我自己绕晕...

  1. 输入
// 接收2个参数
// ((* → *), [(y → z), (x → y), …, (o → p), ((a, b, …, n) → o)])
// 第一个是函数(* -> *), *指参数不限
// 第二个是数组 [(y → z), (x → y), …, (o → p), ((a, b, …, n) → o)],里面的内容为函数
// 根据观察可以发下数据处理流从右到左, 最后返回z
// (a, b, ..., n) -> o -> p ... -> x -> y -> z
复制代码
  1. 输出
// 返回一个函数 (a,b, ..., n) -> z
// 这是可以和输入的第二个参数做对比,下面是分析输入得出的结果 
// (a, b, ..., n) -> o -> p ... -> x -> y -> z
// 可以发下输入,输入都一样,结合compose这个单词的意思:(组合)
// 可以猜测: 输入(a, b, ..., n) -> 一系列函数处理 输出 z
复制代码

解释的还是不够透彻,大佬可以在评论区留言解释composeWith的类型签名...

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章