FP TS:Applicative - kscarrot/blog GitHub Wiki
既然函子对多参数的支持不够好,那么就要想办法绕过多参数.
首先我们看看这个签名,然后将其柯里化:
g: (args: [B, C]) => D
//currying
g: (b: B) => (c: C) => D
现在我们解决了多个参数的问题,但是需要重写一个lift函数
liftA2(g): (fb: F<B>) => (fc: F<C>) => F<D>
lift(g): (fb: F<B>) => F<(c: C) => D>
事实上,lift2对F<(c: C) => D>
做了一个拆包,转化成了(fc: F<C>) => F<D>
展示一下Apply的签名,ap就是拆包操作
interface Apply<F> extends Functor<F> {
ap: <C, D>(fcd: HKT<F, (c: C) => D>, fc: HKT<F, C>) => HKT<F, D>
}
interface Applicative<F> extends Apply<F> {
of: <A>(a: A) => HKT<F, A>
}
下面是一些用例:
- Example (F = Array)
import { flatten } from 'fp-ts/lib/Array'
const applicativeArray = {
map: <A, B>(fa: Array<A>, f: (a: A) => B): Array<B> => fa.map(f),
of: <A>(a: A): Array<A> => [a],
ap: <A, B>(fab: Array<(a: A) => B>, fa: Array<A>): Array<B> =>
flatten(fab.map(f => fa.map(f)))
}
Ï
- Example (F = Option)
import { Option, some, none, isNone } from 'fp-ts/lib/Option'
const applicativeOption = {
map: <A, B>(fa: Option<A>, f: (a: A) => B): Option<B> =>
isNone(fa) ? none : some(f(fa.value)),
of: <A>(a: A): Option<A> => some(a),
ap: <A, B>(fab: Option<(a: A) => B>, fa: Option<A>): Option<B> =>
isNone(fab) ? none : applicativeOption.map(fa, fab.value)
}
- Example (F = Task)
import { Task } from 'fp-ts/lib/Task'
const applicativeTask = {
map: <A, B>(fa: Task<A>, f: (a: A) => B): Task<B> => () => fa().then(f),
of: <A>(a: A): Task<A> => () => Promise.resolve(a),
ap: <A, B>(fab: Task<(a: A) => B>, fa: Task<A>): Task<B> => () =>
Promise.all([fab(), fa()]).then(([f, a]) => f(a))
}
所以对于两个参数我们重写了lift,3个参数也可以支持
import { HKT } from 'fp-ts/lib/HKT'
import { Apply } from 'fp-ts/lib/Apply'
type Curried2<B, C, D> = (b: B) => (c: C) => D
function liftA2<F>(
F: Apply<F>
): <B, C, D>(g: Curried2<B, C, D>) => Curried2<HKT<F, B>, HKT<F, C>, HKT<F, D>> {
return g => fb => fc => F.ap(F.map(fb, g), fc)
}
type Curried3<B, C, D, E> = (b: B) => (c: C) => (d: D) => E
function liftA3<F>(
F: Apply<F>
): <B, C, D, E>(
g: Curried3<B, C, D, E>
) => Curried3<HKT<F, B>, HKT<F, C>, HKT<F, D>, HKT<F, E>> {
return g => fb => fc => fd => F.ap(F.ap(F.map(fb, g), fc), fd)
}
更多的参数也可以通过featch去实现