在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-12
並且有習題和測試可以讓大家練習。
現在還有一個常用情境用前幾天教的函數無法實現的很優雅,直接看範例。
export type Mice = {
name: string;
rank: number;
award?: string;
};
/** a lot of if else */
export const normalAlt = (mice: Mice) => {
if (mice.award) return `Awarded with ${mice.award}!`;
else if (mice.rank <= 5) return `No. ${mice.rank} mice on the board!`;
else if (mice.rank <= 10) return `In top 10 mouses at ${mice.rank}!`;
else return 'Is on sale!';
};
前幾天學習到的 getOrElse
以及 match
拿來實現 if...else
的場景會稍嫌繁瑣,所以我們需要建立一個 alt
函式,來表示前一個 Option
失敗(None
)後的路徑。
type Alt = <A>(that: () => O.Option<A>) => (me: O.Option<A>) => O.Option<A>;
export const alt: Alt = that => me => O.isNone(me) ? that() : me;
如果 Option
為 Some
的話,則繼續往下傳,為 None
的話則運算 that
函式,接下來嘗試使用 Alt
改寫上述函式。
export const getMiceAward = (mice: Mice) => pipe(
mice.award,
O.fromNullable,
O.map(award => `Awarded with ${award}!`),
);
export const getMiceTop5 = (mice: Mice) => pipe(
mice,
O.fromPredicate(({ rank }) => rank <= 5),
O.map(({ rank }) => `No. ${rank} mice on the board!`),
);
export const getMiceTop10 = (mice: Mice) => pipe(
mice,
O.fromPredicate(({ rank }) => rank <= 10),
O.map(({ rank }) => `In top 10 at ${rank}!`),
);
// use { name: 'mice', rank: 10 } as an example
export const fpAlt = (mice: Mice) => pipe(
mice, // { name: 'mice', rank: 10 }
getMiceAward, // { _tag: 'None' }
O.alt(() => getMiceTop5(mice)), // { _tag: 'None' }
O.alt(() => getMiceTop10(mice)), // { _tag: 'Some', value: 'In top 10 at 10!' }
O.getOrElse(() => 'Is on sale!'), // 'In top 10 mouses at 10!'
);
大家可能也有發現 fromNullable
, fromPredicate
等等的功能,之後我們再進行介紹。
今天的主題在 should-i-use-fp-ts src/day-12
有習題和測試可以練習,大家可以嘗試自己能不能寫出自己的 alt
, altW
。