函数式编程心得2

204 阅读4分钟

函数组合概念

函数组合:就是一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数,合并成一个函数

  • 函数就像多个数据管道,把管道组合起来,让数据穿过管道得到最终结果
  • 函数组合是从右到左执行
//例如下面代码
const { compose, curry } = require('folktale/core/lambda');
const { toUpper, first  } = require('lodash')
let g = compose(toUpper,first)
console.log(g(['one','two']));  //ONE

loadsh中的组合函数使用

  1. loadsh中组合函数flow()或者flowRight(),他们都可以组合多个函数
  2. flow()是从左到右,flowRight是从右到左(使用多一些)

lodash的FP模块(fp模块提供的方法都是柯里化的)

lodash是数据优先,函数之后,fp模块是函数优先,数据之后

Pointfree

我们可以吧数据处理的过程定义成与数据无关的合成运算,不需要用到代表那个参数,只要简单的运算步骤合成到一起 在使用这种模式之前我们需要定义一些辅助的基本运算

  1. 不需要指明处理的数据
  2. 只需要合成运算过程,
  3. 需要定义一些辅助的基本运算函数
//将word wild weeb 改成 W. W. W.
let fu = fp.flowRight(fp.join('. '),fp.map(fp.flowRight(fp.toUpper,fp.first)),fp.split(' '))
console.log(fu('word wild weeb'))

Functor(函子)

需要把函数式编程中的副作用,在可控的范围内,处理异常操作,异步操作。此时我们需要用函子。 函子可以看成特殊的容器。通过一个普通的对象来实现,自定义一些方法来处理函数式编程的副作用

//普通的函子
class list{
  static of(value){
      return new list(value)
  }
  //维护一个不对外公布的值
  constructor(value){
      this._value = value
  }
  map(fun){
      //返回新的处理函子
      return list.of(fun(this._value))
  }
}
let r = list.of()
        .map(x=>x+1)
        .map(x=>x*x)
//函子的值是不会拿出来的,永远保存在内部,不对外公布的。        
console.log(r);  //list {_value : 36}      

函数式编程的运算不直接操作值,是由函子完成的 函子就是一个实现map的对象 可以把函子想象成一个封装的盒子 想要处理盒子中的值,我们需要给盒子的map方法传递一个处理的纯函数,有这个函数来处理 最终map返回一个新的盒子(函子)

MayBe(函子)

MayBe函子处理传入的 null或者undefine

//普通的函子
class MayBe{
  static of(value){
      return new MayBe(value)
  }
  //维护一个不对外公布的值
  constructor(value){
      this._value = value
  }
  map(fun){
      //返回新的处理函子
      return this.isNithing() ? MayBe.of(null) : MayBe.of(fun(this._value)) 
  }
  isNithing(){
      return this._value === null || this._value === undefined
  }
}
let r = MayBe.of()
        .map(x=>null)
        .map(x=>x*x)
//函子的值是不会拿出来的,永远保存在内部,不对外公布的。        
console.log(r);  //MayBe {_value : null}      

Either(函子)

Eitherh函子是类似于if….else…来处理 异常会让函数变得不纯,eiither函子可以用来异常处理

class left{
  static of(value){
    return new left(value)
  }
  constructor(value){
    this._value =value
  }
  map(fun){
    //返回当前对象
    return this
  }
}
class right{
  static of(value){
    return new right(value)
  }
  constructor(value){
    this._value =value
  }
  map(fun){
    return right.of(fun(this._value))
  }
}
function parseJSON(str){
  try{
    return right.of(JSON.parse(str))
  }catch(e){
    return left.of({error: e.message})
  }
}
// let r = parseJSON('{name:zs}')
// console.log(r);  //left {_value: { error: 'Unexpected token n in JSON at position 1' } }

let r = parseJSON('{"name":"zs"}')
        .map(x=> x.name.toUpperCase())
console.log(r); //right { _value: 'ZS' }

IO(函子) —>input.output

io函子中的_value 是一个函数,这里吧函数当作值来处理 io函子可以吧不纯的动作储存到_value中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯 把不纯的操作交给调用者来处理

class IO{
  static of(value){
    return new IO(function(){
      return value
    })
  }
  constructor(fun){
    this._value = fun
  }
  map(fun){
    //此处使用 new IO的构造函数,是因为要吧当前函子的(value)函数,和传入的函数,组成新的函数 
    return new IO(fp.flowRight(fun,this._value))
  }
}
let r = IO.of(process).map(x=>x.execPath)
console.log(r); //==> IO { _value: [Function] }
console.log(r._value()); // ==>/usr/local/bin/node

FolkTale库配合task函子来使用(FolkTale==>处理一些异步操作)

const { task } = require('folktale/concurrency/task')
const fs = require('fs')
const { split,find } = require('lodash/fp')
function readFile(filename) {
  return task(resolver=>{ 
    fs.readFile(filename,'utf-8',(err,data)=>{
      if(err) resolver.reject(err)

      resolver.resolve(data)
    })
  })
}
readFile('package.json')
  .map(split('\n'))
  .map(find(x=> x.includes('version')))
  .run()
  .listen({
    onRejected:err =>{
      console.log(err);
    },
    onResolved:value=>{
      console.log(value); //  "version": "1.0.0",
    }
  })

Monad函子问题(解决函子嵌套的问题)

是具有静态的io 方法,且具有 join的一个函子,并且有flatMap方法

const fp = require('lodash/fp')
const fs = require('fs') 
class IO{
  static of(value){
    return new IO(function(){
      return value
    })
  }
  constructor(fun){
    this._value = fun
  }
  map(fun){
    //此处使用 new IO的构造函数,是因为要吧当前函子的(value)函数,和传入的函数,组成新的函数 
    return new IO(fp.flowRight(fun,this._value))
  }
  join(){
    return this._value()
  }
  flatMap(fun){
    return this.map(fun).join()
  }
}
//linux中 cat是读取文件内容,并且打印出来
//模拟cat 
let readFile = function(filename){
  return new IO(
    function(){
      return fs.readFileSync(filename,'utf-8')
    }
  )
}
let  print = function(x) {
  return new IO(
    function(){
      console.log(x)
      return x
    }
  )
}
let r = readFile('package.json')
        .map(fp.toUpper)
        .flatMap(print)
        .join()

Functor,MayBe,Either,都是保存了一个值 IO函子可以延迟执行一个函数,可以控制父作用 Task就是帮组我们处理异步的任务, folktabel就是一个库。 Monad就是帮我们提供解决嵌套问题

参考