Question

$\pi$

$\sum_{n=0}^100n^2$

$\sqrt{a^2+b^2}=c^2$

test latex equation

chain 就是 compose(join,map)

functor: 算子

A functor is a container of type a that, when subjected to a function that maps from a→b, yields a container of type b.我们的 map 函数就是一个算子。

container

一个容器可以放任何东西,甚至是另一个容器.比如我们的数组就是这种容器。

map:

是传入一个 function,把 function 应用到 container 里的值,然后返回这个 container

1
2
3
Container.prototype.map = f => Container.of(f(this.$value))
// 数组的角度
[1,2,3].map(x=> x+1)

Maybe

是一种值可能是 null/undefined 的容器,如果是 null/undefined 时会做特殊处理,比如 map 时就不执行 f,直接返回自己.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Maybe {
	static of(x) {
		return new Maybe(x);
	}
	isNothing = this.$value === null || this.$value === undefined;

	constructor(x) {
		this.$value = x;
	}

	map(fn) {
		return this.isNothing ? this : Maybe.of(fn(this.$value));
	}
	// array
	[1, null, undefined].map(x => x+1) // => [2, null, undefined]

Either

比 Maybe 更 general 的容器,它是两种可能的容器中和一种,左和右(默认右值)。这两个容器有各自的处理逻辑,主要用于做错误处理。Either(string, number)表示这个容器或者是 Left(string)或者是 Right(number).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
static of(x) {
	return new Right(x);
}
constructor(x) {
	this.$value = x;
}
}

class Left extends Either {
	map(f) {
		return this;
	}
}
class Right extends Either {
	map(f) {
		return Either.of(f(this.$value));
	}
}

IO

IO 是一个里面放了一个函数的容器,这个函数不用是纯函数,IO 是用来封装副作用的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class IO {
	static of(x) {
		return new IO(() => x);
	}

	constructor(fn) {
		this.$value = fn;
	}

	map(fn) {
		return new IO(compose(fn, this.$value));
	}
}

Task

Task 是一种 IO,它内部的 func(又叫 fork)有特定的样子(reject, resolve) => {…}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Task {
	constructor(fork) {
		this.fork = fork;
	}

	static of(x) {
		return new Task((_, resolve) => resolve(x));
	}

	static rejected(x) {
		return new Task((reject, _) => reject(x));
	}

	ap(f) {
		return this.chain(fn => f.map(fn));
	}

	chain(fn) {
		return new Task((reject, resolve) => this.fork(reject, x => fn(x).fork(reject, resolve)));
	}

	inspect() { // eslint-disable-line class-methods-use-this
		return 'Task(?)';
	}

	getType() { // eslint-disable-line class-methods-use-this
		return '(Task ? ?)';
	}

	join() {
		return this.chain(x => x);
	}

	map(fn) {
		return new Task((reject, resolve) => this.fork(reject, compose(resolve, fn)));
	}
}

理论

下面这些是等价的:

1
2
map(id) === id
compose(map(f), map(g)) === map(compose(f, g))

./FP学习笔记/map_equal.png

算子互换

2 个 functor 如果可以互换,我们就说他们是 Isomorphic 。因为互换中他们没有信息丢失。 如 Promise 和 Task 就是 Isomorphic

1
2
3
4
5
// promiseToTask :: Promise a b -> Task a b
const promiseToTask = x => new Task((reject, resolve) => x.then(resolve).catch(reject));

// taskToPromise :: Task a b -> Promise a b
const taskToPromise = x => new Promise((resolve, reject) => x.fork(reject, resolve));

Natural transformations 是在算子上的函数。

An introduction for FP to Js User

pure function:

  1. functions
  2. same input and same output
  3. replace the value and function

impure world

monad

interpreter pattern

Algebraic Data Type & Monads & Functors

Algebraic Data Type: Either, Reader, Writer, IO, Option Monads: has map and flatMap Functor: functions to convert a value from one strategy to another strategy plethora demystify honed