函数式编程 - 容器(container)

近来一向在进修函数式编程,前面引见了函数式编程中非常重要的两个运算函数柯里化函数组合,下文涌现的currycompose函数能够夙昔两篇文章中找到。它们都能够直接在现实开辟中用到,写出函数式的顺序。

本文重要初探容器的相干观点,以及怎样处置惩罚编程中异步操纵毛病处置惩罚等依靠外部环境状况变化的状况,,

容器(container)

容器能够设想成一个瓶子,也就是一个对象,内里能够放种种差别范例的值。想一想,瓶子另有一个特征,跟外界离隔,只需从瓶口才拿到内里的东西;类比看看, container 回暴露出接供词外界操纵内部的值。

一个典范的容器示例:

    var Container = function(x) {
        this.__value = x;
    }
    
    Container.of = function(x) {
        return new Container(x);
    }
    
    Container.of("test")   
    // 在chrome下会打印出 
    // Container {__value: "test"}  

我们已完成了一个容器,而且完成了一个把值放到容器内里的 Container.of要领,简朴看,它像是一个应用工场形式建立特定对象的要领。of要领恰是返回一个container。

函子(functor)

上面容器上定义了of要领,functor的定义也相似

Functor 是完成了
map函数并恪守一些特定划定规矩的容器范例。

把值留在容器中,只能暴露出map接口处置惩罚它。函子是非常重要的数据范例,背面会讲到种种差别功用的函子,对应处置惩罚种种依靠外部变量状况的题目。

Container.prototype.map = function(f) {
    return Container.of(f(this.__value))
}

把行将处置惩罚容器内变量的函数,包裹在map要领内里,返回的实行效果也会是一个Container。
如许有几点优点:

  1. 保证容器内的value一向不会暴露出去,
  2. 对value的操纵要领最终会交给容器实行,能够决议什么时候实行。
  3. 轻易链式挪用
    // 应用上一篇中讲到的柯里化,就能够看出其特征。
    var add2 = function(x, y) {
        return x + y;
    };
    
    curriedAdd = curry(add2);
    
    Container.of(2).map(curriedAdd(3));
    // Container {__value: 5}

差别范例的函子

maybe

容器在处置惩罚内部值时,常常碰到传入参数非常的状况的状况,搜检value 值的合理性就非常重要。Maybe 函子保证在挪用传入的函数之前,搜检值是不是为空。

var Maybe = function(x) {
  this.__value = x;
}

Maybe.of = function(x) {
  return new Maybe(x);
}

Maybe.prototype.isNothing = function() {
  return (this.__value === null || this.__value === undefined);
}

Maybe.prototype.map = function(f) {
  return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value));
}
   

这个通用的例子,表现了输出效果的不确定性,也能够看出,在容器内部一切对value值的操纵,都邑交给容器来实行。在value 为空的状况下,也会返回包裹着null的容器,防止后续容器链式挪用报错。

非常捕捉函子

平常 运用throw/catch就能够捕捉非常,抛出毛病,但它并非一种纯函数要领,最好的要领是在涌现非常时,能够平常返回信息。Either函子,内部两个子类Leftright; 能够算作右值是平常状况下运用并返回的值,左值是操纵简朴的默认值。

var Left = function(x) {
  this.__value = x;
}

Left.of = function(x) {
  return new Left(x);
}

Left.prototype.map = function(f) {
  return this;
}

var Right = function(x) {
  this.__value = x;
}

Right.of = function(x) {
  return new Right(x);
}

Right.prototype.map = function(f) {
  return Right.of(f(this.__value));
}

// 输入数据举行校验
var setage = function(age) {
     return typeof age === 'number'? Right.of(age): Left.of('error age')
}

setage(12).map(function(age){return 'my age is' + age})
// Right {__value: "my age is12"}
setage("age").map(function(age){return 'my age is' + age})
// Left {__value: "error age"}

leftright 唯一的区分在于map 要领的完成,固然,一个函子最大的特征也表现在map要领上,
Left.map 不论传入的是什么函数,直接返回当前容器;Right.map则是示例内里的要领一样。

IO 操纵

IO 操纵自身就是不纯的操纵,生来就得跟外界环境变量打交道,不过能够掩饰他的不确定性。跟下面localStorage包裹函数相似,耽误实行IO 操纵。

 var getStorage = function(key) {
    return function() {
        return localStorage[key];
    }
}

再看看,封装了高等一点的IO 函子:

    var IO = function(f) {
        this.__value = f;
    }
    
    IO.of = function(x) {
        return new IO(function(){
            return x;
        })
    }
    
    IO.prototype.map = function(f) {
        // 运用上一句定义的compose函数
        return new IO(compose(f, this.__value))
    }

compose函数组合,内里寄存的都是函数,this.__value跟其他函子内部值差别,它是函数。IO.of要领在new对象之前,把值包裹在函数内里,试图耽误实行。

  // 测试一下
var io__dom= new IO(function() {return window.document})

io__dom.map(function(doc) { return doc.title})

// IO {__value: ƒ}

返回一个没有实行的函数对象,内里的__value值对应的函数,在上面函数挪用后并没有实行,只需在挪用了this.__value值后,才实行。末了一步不纯的操纵,交给了函数挪用者去做。

Monad

一个functor, 只需他定义了一个join 要领和一个of 要领,那末它就是一个monad。 它能够将多层雷同范例的嵌套扁平化,像剥洋葱一样。关键在于它比平常functor 多了一个join 要领。 我们先看看剥开一层的join要领。

    var IO = function(f) {
        this.__value = f
    }
    
    IO.of = function(x) {
        return new IO(function(){
            return x;
        })
    }
    
    IO.prototype.join = function() {
        return this.__value ? this.__value(): IO.of(null);
    }
    // 包裹上两层
    var foo = IO.of(IO.of('test bar'));
    foo.join().__value();
    // 返回内里嵌套着的IO类。 IO {__value: ƒ},接着只需挪用这里的__value(),就能够返回字符串`test bar`;

转头看看前面map要领,return new IO(),天生新的容器,轻易链式挪用,跟 join要领连系一同运用,天生容器后,再扁平化。构成 chain 函数

  var  chain = curry(function(f, m) {
        return m.map(f).join();
    })

看一个完全示例,个中currycompose,离别用到了链接内里的完成,:

var IO = function(f) {
  this.__value = f;
}

IO.of = function(x) {
  return new IO(function() {
    return x;
  })
}

IO.prototype.map = function(f) {
  // 运用上一句定义的compose函数
  return new IO(compose(f, this.__value))
}

IO.prototype.join = function() {
  return this.__value ? this.__value() : IO.of(null);
}

var chain = curry(function(f, m) {
  return m.map(f).join();
})

var log = function(x) {
  return new IO(function() {
    console.log(x);
    return x;
  })
}

var setStyle = curry(function(sel, props) {
  return new IO(function() {
    return document.querySelector(sel).style.background = props
  })
})

var getItem = function(key) {
  return new IO(function() {
    return localStorage.getItem(key);
  })
};

var map = curry(function(f, functor) {
  return functor.map(f);
});

// 简朴完成join
var join = function(functor) {
  return functor.join();
}

localStorage.background = '#000';

var setItemStyle = compose(join, map(setStyle('body')), join, map(log), getItem);

// 换成 链式挪用。
setItemStyle = compose(chain(setStyle('body')), chain(log), getItem);

setItemStyle('background').__value(); // 操纵dom 转变背景色彩

总结

本文重要应用简朴代码举例,引见了容器,函子等相干观点,开端认识了种种差别的函子。深切实践示例,能够参考浏览下面链接:

  1. 函数式编程作风
  2. js函数式编程指南https://llh911001.gitbooks.io…
  3. JavaScript函数式编程(二)
  4. JavaScript:函数式编程基础观点进修
  5. JS函数式编程 – 函子和领域论
  6. javascript函数式编程之 函子(functor)
  7. 函数式编程入门教程
    原文作者:不可能的是
    原文地址: https://segmentfault.com/a/1190000018414407
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞