表題のとおりで、fp-minimalというライブラリを作りました。
モチベーション
まず例として、メールアドレスとパスワードを入力するログイン機能を考えます。それはおそらく、以下のように書くことができます。
import { pipe } from 'fp-minimal';
const login = pipe(validate, getToken, responseToState, redirect)
このように、小さな関数(validate
, getToken
, responseToState
, redirect
)を組み合わせて機能を作る際に、可読性を上げることができます。
概要
fp-minimal
のAPIはpipe
とcurry
のみで、サイズも約1.6KBとなっています。
(compose
をつくらなかったのはminimal
という名を守るため)。
Ramdaのようなライブラリを入れると、全てをRamdaでなんとかしようとしてしまい、場合によっては効率の悪いコードを書いてしまうときもあるかもしれません。愚直に小さい関数を組み合わせてアプリケーションをつくりたいというだけなら、これらのAPIで十分である、というのが想いです。
実装
小さいので、コードの中身を紹介します。
pipe
export function pipe(...args: Function[]): any {
// reduceRightにするとcomposeになる
return (input: any) => args.reduce((result, next) => next(result), input);
}
curry
export function curry(func: Function) {
return function curried(...args: any[]) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2: any[]) {
return curried.apply(this, args.concat(args2));
};
}
};
}
型は力技…。
export function curry<T1, R1>(func: (a: T1) => R1): (a: T1) => R1;
export function curry<T1, T2, R1>(func: (a: T1, b: T2) => R1): (a: T1) => (b: T2) => R1;
export function curry<T1, T2, T3, R1>(func: (a: T1, b: T2, c: T3) => R1): (a: T1) => (b: T2) => (c: T3) => R1;
export function curry<T1, T2, T3, T4, R1>(func: (a: T1, b: T2, c: T3, d: T4) => R1): (a: T1) => (b: T2) => (c: T3) => (d: T4) => R1;
export function curry<T1, T2, T3, T4, T5, R1>(func: (a: T1, b: T2, c: T3, d: T4, e: T5) => R1): (a: T1) => (b: T2) => (c: T3) => (d: T4) => (e: T5) => R1;
使用例
使う時はこのように
import { pipe } from 'fp-minimal';
const double = (x: number) => x * 2;
const addOne = (x: number) => x + 1;
const square = (x: number) => x * x;
pipe(double, addOne, square)(2) // 25
import { curry } from 'fp-minimal';
const add = (x: number, y: number) => x + y;
const addOne = curry(add)(1);
addOne(2); // 3
[1, 2, 3].map(addOne); // [2, 3, 4]
型も推論されています。
返り値の型が変わる場合
curry
curry化した関数で新しい関数を作った場合
おわりに
小さいパーツつなぎあわせてソフトウェアを動かすのは楽しいですね。