基于JavaScript的一些函数式编程观点解说

原文地点
译者的Github 系列文章地点
本文原作者还没有悉数完成,有兴致的可以到原文或许译文地点关注更新

Functional Programming Jargon:函数式编程术语诠释

本文的重要目标等于愿望可以有一种通俗易懂的体式格局来论述函数式编程中罕见的理论术语观点

Arity:参数数目

Arity代指一个函数的参数数目,该关键字来源于类似于unary、binary、ternary等等,由两个后缀-ary-ity构成。比如,假如一个函数许可输入两个参数,那就称为所谓的binary function(二元函数),或许一个有两个参数的函数。有时刻这类函数也会被喜好拉丁语法的人称为”dyadic(二价的)”函数。以此类推,不定参数的方程也就被称为variadic(可变参数函数)

const sum = (a, b) => a + b;

const arity = sum.length;
console.log(arity);
// => 2
// The arity of sum is 2

Higher-Order Functions (HOF):高级函数

一个吸收某个函数作为参数的函数成为高级函数,该函数可以挑选返回一个函数也可以返回其他范例

const filter = (pred, xs) => {
  const result = [];
  for (var idx = 0; idx < xs.length; idx += 1) {
    if (pred(xs[idx])) {
      result.push(xs[idx]);
    }
  }
  return result;
};
const is = type => x => Object(x) instanceof type;
filter(is(Number), [0, '1', 2, null]); //=> [0, 2]

Partial Application:部份封装

将底本一个多参数值的函数封装为牢固参数数目标函数的历程称为Partial Application

let sum = (a, b) => a + b;

// partially applying `a` to `40`
let partial = sum.bind(null, 40);

// Invoking it with `b`
partial(2); //=> 42

Currying

将一个N参数值的函数转化为N个一元函数的组合,Currying与Partial Application的区分在于Partial Application终究天生的函数许可吸收多个值,而Currying天生的函数序列中的每一个函数只许可吸收一个参数

let sum = (a, b) => a + b;

let curriedSum = (a) => (b) => a + b;

curriedSum(40)(2) // 42.

Composition:组合

觉得有点像设想形式里的Decorator,即可以将两个指定范例组合转化为一个新值的函数

最罕见的组合等于罕见的函数组合,许可你将差别的函数组合成一个返回单值的函数

const compose = (f, g) => a => f(g(a)) // Definition
const floorAndToString = compose((val)=> val.toString(), Math.floor) //Usage
floorAndToString(121.212121) // "121"

Purity:纯函数

一个没有任何副作用,而且返回值只由输入决议的函数成为纯函数

let greet = "yo";

greet.toUpperCase(); // YO;

greet // yo;

As opposed to:

let numbers = [1, 2, 3];

numbers.splice(0); // [1, 2, 3]

numbers // []

Side effects:副作用

假如一个函数,除了返回值以外,还会修正某些别的状况,或许与外部函数等有可观察的交互

console.log("IO is a side effect!");

Idempotency:幂等性

屡次实行下都不会发生副作用的函数被称为具有幂等性的函数

f(f(x)) = f(x)

Math.abs(Math.abs(10))

Point-Free Style

那些并没有线性定义参数的函数作风被称为Point-Free Style,这范例每每须要currying 或许 Higher-Order functions

// Given
let map = fn => list => list.map(fn);
let add = (a, b) => a + b;

// Then

// Not points-free - `numbers` is an explicit parameter
let incrementAll = (numbers) => map(add(1))(numbers);

// Points-free - The list is an implicit parameter
let incrementAll2 = map(add(1));

incrementAll明确规定了参数numbers, 而incrementAll2是关于参数的封装,并没有显性申明numbers参数,因而它可以被称为Points Free。平常来说,Points-free的函数都不会用罕见的function或许=>关键字来定义。

Contracts

暂无

Guarded Functions

暂无

Categories:分类

关联到遵照某些划定规矩的函数的对象,比如monoid

Value:值

盘算中常用到的一些复合值(complex)或许简朴值(primitive),包括函数。平常来说,函数式编程中的值都被认为是不可变值。

5
Object.freeze({name: 'John', age: 30}) // The `freeze` function enforces immutability.
(a) => a

注重,比如Functor, Monad如许包括其他值的组织体自身也是值,这就是说,这些复合值也可以互相包括。

Constant:常量

关于一个值的不可变援用,不能跟变量相殽杂。Variable即指那些可能在恣意点呗变动的援用。

const five = 5
const john = {name: 'John', age: 30}

常量平常认为是通明的,也就是说,它们可以被值自身替代而不影响终究的盘算效果,上面的两个常量也可以用下述体式格局表述:

john.age + five === ({name: 'John', age: 30}).age + (5)

上述表达式会一向返回真。

Functor

Functor即指那些可以援用map函数的对象,JavaScript中最简朴的函数就是Array

[2,3,4].map( n => n * 2 ); // [4,6,8]

假定func组织为一个完成了map函数的对象,fg则是恣意的函数,只需func遵照以下划定规矩就可以将func称为一个functor:
Let func be an object implementing a map function, and f, g be arbitrary functions, then func is said to be a functor if the map function adheres to the following rules:

func.map(x => x) == func

以及

func.map(x => f(g(x))) == func.map(g).map(f)

我们将Array称为Functor,也是由于它遵照了以下划定规矩:

[1, 2, 3].map(x => x); // = [1, 2, 3]

以及

let f = x => x + 1;
let g = x => x * 2;

[1, 2, 3].map(x => f(g(x))); // = [3, 5, 7]
[1, 2, 3].map(g).map(f);     // = [3, 5, 7]

Pointed Functor

完成了of要领的Functor,Of会将任何单值转化为一个Functor

Pointed Functor在Array中的完成为:

  Array.prototype.of = (v) => [v];
  
  [].of(1) // [1]

Lift

Lift很类似于map,不过它可以用于多个Functors:

在单值函数下,Map与Lift的作用是一致的:

lift(n => n * 2)([2,3,4]); // [4,6,8]

而Lift可以许可输入多个值:

lift((a, b)  => a * b)([1, 2], [3]); // [3, 6]

Referential Transparency:通明援用

一个可以直接用其值来替代而不会影响到顺序表现的表达式称为通明援用

比如我们有一个叫greet的援用

let greet = () => "Hello World!";

任何关于greet()的挪用都可以被Hello World!直接替代,因而可以将greet称为通明援用。

Equational Reasoning

当一个运用由表达式组合而成而且没有任何副作用的时刻,该体系可以由部份推导而来

Lazy evaluation:懒盘算

Lazy evaluation 等于所谓的只要在须要某个值的时刻才举行盘算的机制。在函数式语言中,这个机制就许可关于那些近乎无穷的列表举行操纵。

let rand = function*() {
    while(1<2) {
        yield Math.random();
    }
}
let randIter = rand();
randIter.next(); // Each exectuion gives a random value, expression is evaluated on need.

Monoid:独异点

一个monoid就是与某个恒等值举行组合以后不会影响现有效果的数据范例

一个最简朴的Monoid就是以下所示:

1 + 1; // 2

数据范例是number,函数是+

1 + 0; // 1

恒等式的值是0,将0与任何数相加并不会转变值。有时刻,monoid范例举行差别的交流操纵也不会影响效果:

1 + (2 + 3) == (1 + 2) + 3; // true

数组衔接也可以认为是一个monoid:

[1, 2].concat([3, 4]); // [1, 2, 3, 4]

恒等值等于空数组: []

[1, 2].concat([]); // [1, 2]

Monad

一个Monad就是具有of以及chain函数的对象。 Chain 类似于 map只不过它会扁平化终究求得的嵌套式效果。

['cat,dog','fish,bird'].chain(a => a.split(',')) // ['cat','dog','fish','bird']

//Contrast to map
['cat,dog','fish,bird'].map(a => a.split(',')) // [['cat','dog'], ['fish','bird']]

You may also see of and chain referred to as return and bind (not be confused with the JS keyword/function…) in languages which provide Monad-like constructs as part of their standard library (e.g. Haskell, F#), on Wikipedia and in other literature. It’s also important to note that return and bind are not part of the Fantasy Land spec and are mentioned here only for the sake of people interested in learning more about Monads.

Comonad:余票据

完成了extractextend函数的对象

let CoIdentity = v => ({
    val: v,
    extract: this.v,
    extend: f => CoIdentity(f(this))
})

Extract 可以将值从Functor中吐出来:

CoIdentity(1).extract() // 1

Extend则是会返回一个跟Commonad雷同值的函数:

CoIdentity(1).extend(co => co.extract() + 1) // CoIdentity(2)

Applicative(可实用的) Functor

一个Applicative Functor就是一个完成了ap函数的对象,Ap可以将某个对象中的某个值转化为另一个对象中的雷同范例的值

[(a)=> a + 1].ap([1]) // [2]

Morphism:态射

一个转化函数

Isomorphism:同态转换

用差别体式格局存储的可以表明雷同数据的转换

比如,一个二维的数组可以存储为数组:[2,3]或许对象:{x: 2, y: 3}

// Providing functions to convert in both directions makes them isomorphic.
const pairToCoords = (pair) => ({x: pair[0], y: pair[1]})

const coordsToPair = (coords) => [coords.x, coords.y]

coordsToPair(pairToCoords([1, 2])) // [1, 2]

pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2}

Setoid

完成了equals函数的对象,即可以与其他对象举行对照推断是不是属于统一范例,被称为Setoid。

下面关于原型的扩大可以将Array变成Setoid。

Array.prototype.equals = arr => {
    var len = this.length
    if (len != arr.length) {
        return false
    }
    for (var i = 0; i < len; i++) {
        if (this[i] !== arr[i]) {
            return false
        }
    }
    return true
}

[1, 2].equals([1, 2]) // true
[1, 2].equals([0]) // false

Semigroup:半群

一个具有concat,行将另一个对象转化为雷同范例的函数,函数的对象称为Semigroup。

[1].concat([2]) // [1, 2]

Foldable:可摺叠

完成了reduce函数,即可以将一个对象转化为其他范例的函数,的对象称为Foldable对象。

let sum = list => list.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]) // 6

Traversable

暂无

Type Signatures:范例署名

平常来说,函数都邑解释表明它们的参数范例和返回值范例

// functionName :: firstArgType -> secondArgType -> returnType

// add :: Number -> Number -> Number
let add = x => y => x + y

// increment :: Number -> Number
let increment = x => x + 1

假如一个函数吸收其他函数作为参数,比如如许:

// call :: (a -> b) -> a -> b
let call = f => x => f(x)

这里的a, b, c, d表明参数可所以恣意范例,不过它会将范例a转化为另一个范例b,而关于下面这个map,它的解释表清楚明了它会输入一个a范例的列表,然后转化为另一个包括了b范例的列表。

// map :: (a -> b) -> [a] -> [b]
let map = f => list =>  list.map(f)
    原文作者:王下邀月熊_Chevalier
    原文地址: https://segmentfault.com/a/1190000005595107
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞