[译]函数式JavaScript之Functors

Functors

先看看以下代码:

function plus1(value) {  
    return value + 1;
}

这就是一个一般函数,吸收一个integer作为参数,再加1返回。相似的,我们还能再来一个加2的函数。稍后我们会用到这几个函数:

function plus2(value) {  
    return value + 2;
}

下面我们来写一个以下的组合函数,来按需实行上述函数

function F(value, fn) {  
    return fn(value);
}

F(1, plus1) ==>> 2

当传入准确的integer参数时,这个组合函数F事情一般,那假如传入的数据范例是Array呢?

F([1, 2, 3], plus1)   ==>> '1,2,31'

我擦,我们给了一个Array of integers,想让她们和plus1中的数值相加,效果返回的是个string?!效果不对不说,我们是以Array开头儿的,效果返回一个string,范例也没对上啊!换句话说,我们这个顺序把输入的构造也给整岔劈了。我们照样愿望函数F能做“准确的事儿”-保护输入参数的数据构造,并使其被各handler准确处置惩罚。

OK,我这儿说的“保护输入参数的数据构造”是?这个函数F应当把传入的Array拆开并获得个中的每一个值。然后顺次交给handler处置惩罚。然后把handler处置惩罚后的各个效果再封装成一个新的Array,然后返回这个新Array。好消息是我这一堆空话说的东西都不用你写了,JavaScript已写好这么一个函数了,她叫map

[1, 2, 3].map(plus1)   ==>> [2, 3, 4]

map就是一个functor

functor就是一个函数,吸收一个数值、一个handler,然后做事儿!

再说细点儿

functor就是一个函数,吸收一个数值、一个handler,拆开传入的原始值,获得个中的各个剖析后的值,然后挪用handler顺次处置惩罚每一个上一步的到的数据,再将处置惩罚后的多个数据再封装成一个新的构造体,末了返回这个新的构造体。

这里要注重传入值的范例,拆开后的各个数值多是原始数据范例,也多是鸠合。

而且,末了返回的数据范例也没必要一定和传入的数据范例如出一辙。在我们上面的例子里,map的输入和返回值是一样的数据范例,都是Array。返回的数据构造可所以恣意构造,只需能离别猎取个中的数值即可。所以,假定你有一个函数,吸收一个Array作为参数,但返回一个包含了keysObject,每一个key都指向一个对应的数值,那这也是一个functor

JavaScript里,filter就是一个functor,因为她照旧返回一个Array,但forEach就不是,因为她返回undefined。这就是我们说的,forEach没有“保护输入参数的数据构造”。

Functors是数学里关于”homomorphisms between categories”的观点,不懂没紧要,我们离别换个词儿再读一下:

  • homo = 一些、多个

  • morphisms = 保护数据构造的函数

  • category = 范例

依据上述辞汇剖析,函数F能够看做是两个一般函数fg的组合。

F(f . g) = F(f) . F(g)

个中.就是示意组合。即:functors必需保留组合特征。

基于这个方程式,我们就可以得出一个函数是不是是functor的结论。

Array Functor

适才我们看到map就是一个操纵Arrayfunctor。下面我们来证实一下Array.map就是一个functor

function compose(f, g) {
    return function(x) {
        return f(g(x));
    };
}

组合多个函数就是经由过程将前一个函数的实行效果传给后一个函数作为参数的情势来顺次挪用多个函数。注重:我们上面这个compose是从右向左实行的,将g的实行效果传给f

[1, 2, 3].map(compose(plus1, plus2))   ==>> [ 4, 5, 6 ]

[1, 2, 3].map(plus2).map(plus1)        ==>> [ 4, 5, 6 ]

看看!随你怎样写,效果都一样。 所以map就是一个functor

下面我们来尝尝其他functorsfunctors传入参数的范例可所以恣意范例,只需你有方法拆开她的值,然后返回一个新的数据构造。

String Functor

OK,那我们是不是是也能够写一个能处置惩罚stringfunctor

先来文个题目,你能“拆开”一个string么? 必需的呀(这里假如有疑问,就是你的不对了哦),假如你把一个string当作一个Array of chars,是不是是能够拆开了?所以啊,题目就在于你是怎样思索的。

然后,我们也晓得每一个char实在都有一个integerchar code。那我都能够运用上面的plus1操纵每一个char,然后将一切效果封装回string,再返回!

function stringFunctor(value, fn) {  
    var chars = value.split('');
    return chars.map(function(char) {  
        return String.fromCharCode(fn(char.charCodeAt(0)));
    }).join('');
}

stringFunctor("ABCD", plus1) ==>> "BCDE"

最先感受到牛逼之处了么?预计你都能基于string functor写一个XX解析器了。

Function Functor

JavaScript中,函数是一等国民(first class citizens)。意义是你能够像其他任何范例那样运用函数。所以我们能够写一个服务于函数的functor么?

答案是一定的!

但怎样拆开一个函数是个题目!简朴点儿,你能够直接实行这个函数,然后用她的返回值。不过傻子也晓得这一定有题目(实行是须要参数的)!牢记这里我们一定要把传入的函数自身当作谁人传入的“值”。明白了这一点,我们只需再返回一个函数,看上去就是functor了吧?当这个返回的函数实行时,我们传入指定参数,实在她内部是将传入的参数递给value函数,再将value(initial)的效果传给fn,末了的到终究返回值。

function functionFunctor(value, fn) {
    return function(initial) {
        return function() {
            return fn(value(initial));
        };
    };
}

var init = functionFunctor(function(x) {return x * x}, plus1);
var final = init(2);
final() ==> 5

说白了,上面这个Function functor没做什么迥殊的事。但要注重的是,除非你终究实行该functor,不然什么事都不会发作哦!一切东西都被暂存起来,直到你实行终究functor。由Function functor能够衍生出来其他函数式编程的内容,比如:状况保护、一连挪用以至是Promise。有兴致的,能够本身尝试依据已学学问来把这几个观点完成一下。

MayBe Functor

function mayBe(value, fn) {
    return value === null || value === undefined ? value : fn(value);
}

看,这也是个正当的functor

mayBe(undefined, compose(plus1, plus2))     ==>> undefined
mayBe(mayBe(undefined, plus2), plus1)       ==>> undefined
mayBe(1, compose(plus1, plus2))             ==>> 4
mayBe(mayBe(1, plus2), plus1)               ==>> 4

mayBe经由过程了我们上面的测试。这儿还真没有什么拆开和从新封装。假如传入是空,那返回也是空。mayBe是一中简朴有用的途径选择函数,和一下这类写法雷同:

if (result === null) {
    return null;
} else {
    doSomething(result);
}

Identity Function

function id(x) {
    return x;
}

上面这个就是所谓的identity function。她就把传入的参数又返回了一遍。她就是这么叫的,我也没辙,因为在数学盘算里,这就示意组合函数的ID。

前面我们学了functor就是要保留组合特征。实在functor也得保留她的identity

F(value, id) = value

我们来拿map试一下

[1, 2, 3].map(id)    ==>>  [ 1, 2, 3 ]

Type Signature

Type Signature声清楚明了一个函数的参数及返回值的形状。那之前我们写的plus1函数的Type Signature就是:

f: int -> int

map作为functor,她的Type Signature依赖于handlerType Signature。比如:mapplus1组合运用的话,她的Type Signature是:

map: [int] -> [int]

不过,因为handlerType Signature没必要前后一致,以下:

f: int -> string

mapType Signature也可所以:

map: [int] -> [string]

这就是说,范例变化不会影响functor的函数组合特征。一般来说,functorType Signature能够这么定义:

F: A -> B

举例说,就是map能够传入数值数组,然则却返回一个字符串数组,她照旧是functor

Monads是一种特别范例的functor,定义以下:

M: A -> A

更多内容,且看下回剖析!

原文地点:Functional JavaScript – functors

    原文作者:leftstick
    原文地址: https://segmentfault.com/a/1190000004604628
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞