函数式编程

总之,在函数式编程中,函数就是一个管道(pipe)。这头进去一个值,那头就会出来一个新的值,没有其他作用。

一、函数的合成与柯里化

1.1函数的合成

如果一个函数要经过多个函数才可以变成另一个值,这时候就要把函数的中间步骤合并成一个函数,这就叫做函数的合成。

例如:


图片.png
const compose = function (f, g) {
  return function (x) {
    return f(g(x));
  };
}
1.2.柯里化

把一个多参数的函数,转化为单参数的函数

// 柯里化之前
function add(x, y) {
  return x + y;
}

add(1, 2) // 3

// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}

addX(2)(1) // 3

二 ,函子

函数不仅可以用于同一个范畴之中值的转换,还可以用于将一个范畴转成另一个范畴。这就涉及到了函子(Functor)。

2.1函子的概念

函子是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位。
它的变形关系可以依次作用于每一个值,将当前容器变形成另一个容器。

2.1函子的代码实现

任何具有map方法的数据结构,都可以当做函子的实现

class Functor {
  constructor(val) { 
    this.val = val; 
  }

  map(f) {
    return new Functor(f(this.val));
  }
}

上面代码中,Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))。

一般约定,函子的标志就是容器具有map方法。该方法将容器里面的每一个值,映射到另一个容器。


(new Functor(2)).map(function (two) {
  return two + 2;
});
// Functor(4)

(new Functor('flamethrowers')).map(function(s) {
  return s.toUpperCase();
});
// Functor('FLAMETHROWERS')

(new Functor('bombs')).map(_.concat(' away')).map(_.prop('length'));
// Functor(10)

三 、of 方法

函数式编程一般约定,函子有一个of方法,用来生成新的容器。

下面用 of 替换掉 new

Functor.of = function(val) {
  return new Functor(val);
};

这个例子可以改成


Functor.of(2).map(function (two) {
  return two + 2;
});
// Functor(4)

四、Maybe 函子

Maybe 函子的map方法里面设置了空值检查。 防止出现空值而报错


Functor.of(null).map(function (s) {
  return s.toUpperCase();
});
// TypeError

像这个函数就因为有空值所以就报错了

然而 Maybe 函子 就是这样检查的:

class Maybe extends Functor {
  map(f) {
    return this.val ? Maybe.of(f(this.val)) : Maybe.of(null);
  }
}

有了 Maybe 函子,处理空值就不会出错了。

Maybe.of(null).map(function (s) {
  return s.toUpperCase();
});
// Maybe(null)

五、Either 函子

1.Either 函子相当于 if,,,else,,,语句。

Either 函子内部有两个值:左值(Left)和右值(Right)。右值是正常情况下使用的值,左值是右值不存在时使用的默认值。

var addOne = function (x) {
  return x + 1;
};

Either.of(5, 6).map(addOne);
// Either(5, 7);

Either.of(1, null).map(addOne);
// Either(2, null);

上面代码中,如果右值有值,就使用右值,否则使用左值。通过这种方式,Either 函子表达了条件运算。

2.Either 函子的另一个用途是代替try...catch,使用左值表示错误。


    function parseJSON(json) {
      try {
        return Either.of(null, JSON.parse(json));
      } catch (e: Error) {
        return Either.of(e, null);
      }
    }

上面代码中,左值为空,就表示没有出错,否则左值会包含一个错误对象e。一般来说,所有可能出错的运算,都可以返回一个 Either 函子。

五、ap 函子

function addTwo(x) {
  return x + 2;
}

const A = Functor.of(2);
const B = Functor.of(addTwo)

ap 函子就是可以让函子 B 内部的函数,可以使用函子 A 内部的值进行运算。


class Ap extends Functor {
  ap(F) {
    return Ap.of(this.val(F.val));
  }
}

注意,ap方法的参数不是函数,而是另一个函子。

因此,上面的例子可以这样写

Ap.of(addTwo).ap(Functor.of(2))
// Ap(4)

ap 函子的意义在于,对于那些多参数的函数,就可以从多个容器之中取值,实现函子的链式操作。

function add(x) {
  return function (y) {
    return x + y;
  };
}

Ap.of(add).ap(Maybe.of(2)).ap(Maybe.of(3));
//或者这样写
Ap.of(add(2)).ap(Maybe.of(3));
// Ap(5)

六、Monad 函子

函子是一个容器,可以包含任何值。这样就出现了多层嵌套的函子。

Maybe.of(
  Maybe.of(
    Maybe.of({name: 'Mulburry', number: 8402})
  )
)

上面这个函子,一共有三个Maybe嵌套。如果要取出内部的值,就要连续取三次this.val。这当然很不方便,因此就出现了 Monad 函子。

Monad 函子的作用是,总是返回一个单层的函子。它有一个flatMap方法,与map方法作用相同,唯一的区别是如果生成了一个嵌套函子,它会取出后者内部的值,保证返回的永远是一个单层的容器,不会出现嵌套的情况。

class Monad extends Functor {
  join() {
    return this.val;
  }
  flatMap(f) {
    return this.map(f).join();
  }
}

上面代码中,如果函数f返回的是一个函子,那么this.map(f)就会生成一个嵌套的函子。所以,join方法保证了flatMap方法总是返回一个单层的函子。这意味着嵌套的函子会被铺平(flatten)。

七、IO 操作

Monad 函子的重要应用,就是实现 I/O (输入输出)操作。


    var fs = require('fs');

    var readFile = function(filename) {
      return new IO(function() {
        return fs.readFileSync(filename, 'utf-8');
      });
    };

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

上面代码中,读取文件和打印本身都是不纯的操作,但是readFile和print却是纯函数,因为它们总是返回 IO 函子。

如果 IO 函子是一个Monad,具有flatMap方法,那么我们就可以像下面这样调用这两个函数。

readFile('./user.txt')
.flatMap(print)

由于返回还是 IO 函子,所以可以实现链式操作。因此,在大多数库里面,flatMap方法被改名成chain。


var tail = function(x) {
  return new IO(function() {
    return x[x.length - 1];
  });
}

readFile('./user.txt')
.flatMap(tail)
.flatMap(print)

// 等同于
readFile('./user.txt')
.chain(tail)
.chain(print)

你可能感兴趣的:(函数式编程)