在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-16 並且有習題和測試可以讓大家練習。
接下來要介紹的 Do notation 函式是 apS, 用於增加不依賴於 Do 內部屬性的純數值,以下是使用情境。
const example = pipe(
// { _tag: 'Some', value: {} }
O.Do,
// { _tag: 'Some', value: { a: 'tests' } }
O.apS('a', O.some('tests')),
// { _tag: 'Some', value: { a: 'tests', b: 1 } }
O.apS('b', O.some(1)),
// { _tag: 'Some', value: { a: 'tests', b: 1, c: 2 } }
O.apS('c', O.some(2)),
);
和 bind 的差別就只有在 Do notation 新增此屬性時,有沒有使用到 Do 內部屬性的必要。
const diff = pipe(
O.of(1), // { _tag: 'Some', value: 1 }
O.bindTo('x'), // { _tag: 'Some', value: { x: 1 } }
// apS only accept pure variable to be appended
// { _tag: 'Some', value: { x: 1, y: 'apS' } }
O.apS('y', O.of('apS')),
// bind can use the prop inside Do // x, y
// { _tag: 'Some', value: { x: 1, y: 'apS', z: '1 apS' } }
O.bind('z', ({ x, y }) => O.of(`${x} ${y}`)),
);
接著我們來實作 apS 的型別。
name 來作為 Do object 的 prop name(key)name 必須是 Do 尚未有的 keyfb 作為 [name] 這個 key 的 value
Do 來注入新的屬性export type ApS = <N extends string, A, B>(name: Exclude<N, keyof A>, fb: O.Option<B>) =>
<A>(fa: O.Option<A>) =>
O.Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }>;
接著實作函式 apS,實作過程和 bind 非常相似,差別只有傳入的數值是純數值。
// use name = 'a', fb = O.some('tests'), fa = O.Do as example
const apS: ApS = (name, fb) => fa => pipe(
fa, // { _tag: 'Some', value: {} } (our Do object)
O.flatMap(a => pipe(
fb, // { _tag: 'Some', value: 'tests' } (our pure variable)
// { _tag: 'Some', value: { a: 'tests' } }
O.map(b => Object.assign({}, a, { [name]: b }) as any),
)),
); // { _tag: 'Some', value: { a: 'tests' } }
今天的主題在 should-i-use-fp-ts src/day-16 有習題和測試可以練習,大家可以嘗試自己能不能寫出自己的 apS。