Coyoneda
気持ちとしてはmapを遅延評価する感じで,行われた操作を保持するだけ。解釈は別に与える必要がある
素朴な実装ではfmapは内部の持つ関数と新しい関数を合成することによって行われるが,これをすると解釈時に関数適用をしたいときに最古の関数まで辿っていかないといけなくて非効率なので,type-aligned queue のような構造が使われることがある code:coyoneda.ts
// 任意の多相型(のつもり)
// ここにどんな多相型を入れてもFree Functorに入れればmapできる
// Free Functor(Coyonedaとも呼ばれる)
type Coyoneda<A> = CoyonedaImpl<any, A>
const makeCoyoneda = <A>(fa: F<A>): Coyoneda<A> => new CoyonedaImpl(fa, <X>(x: X) => x)
class CoyonedaImpl<X, A> {
constructor(readonly fx: F<X>, readonly f: (x: X) => A) { }
map<B>(f: (x: A) => B): Coyoneda<B> {
return new CoyonedaImpl(this.fx, x => f(this.f(x)))
}
}
const f = makeCoyoneda({ 'hoge': 1, 'piyo': 2 })
const f2 = f.map(x => ${x}) // 定義なしでmapできる
// 解釈1 "普通に"解釈する
const eval1 = <A>(c: Coyoneda<A>): F<A> => {
const result: F<A> = {}
for (const k of Object.keys(c.fx)) {
}
return result
}
console.log(eval1(f2))
// 解釈2 型の上では空オブジェクトを返しても成立
const eval2 = <A>(c: Coyoneda<A>): F<A> => ({})
console.log(eval2(f2))