楔子
程式碼是拿來使用的,今天就來分享一些小工具來幫助寫 code ,這些東西算是函數式編程的入門吧,用久了才會比較有感,今天就當來練練手。
先上個常用的 tool box
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x)
const I = x => x
const Box = x =>
({
chain: f => f(x),
map: f => Box(f(x)),
fold: f => f(x),
inspect: () => `Box(${x})`
})
const Right = x =>
({
chain : f => f(x),
map : f => Right(f(x)),
fold : (f, g) => g(x),
inspect : () => `Right(${x})`
})
const Left = x =>
({
chain : f => Left(x),
map : f => Left(x),
fold : (f, g) => f(x),
inspect : () => `Left(${x})`
})
const fromNullable = x =>
(x === null || x === undefined || x.length !== 0) ? Left(null) : Right(x)
const tryCatch = f => {
try {
return Right(f())
} catch (e) {
return Left(e)
}
}
Null vs undefined
在 javascript 的特性中,最討厭的就是 undefined 了..
在處理資料的時候,常常都要檢查錯誤
(如果沒有檢查習慣的小夥伴,應該就每天活在 debug 的煉獄中了吧)
很常的習慣就大概會像這樣的 code
// 假設隨便取個數字當 3 時就給 undefined 或 null
function getUndefined(x) {
return x === 3 ? undefined : x
}
let checkData = getUndefined(3)
if (checkData === undefined) {
console.log('yo.. x 是 undefined', )
}
// 那如果很多檢查條件呢? null, []
if (checkData === undefined || checkData === null || checkData.length === 0) {
console.log('yo.. x 是 undefined, null, []')
}
// 注意一下如果這個會噴錯哦? why?
// if (checkData.length === 0 || checkData === undefined || checkData === null || ) {}
所以在很多 coding 的過程中都一直在檢查資料的格式是否符合,有時少檢查就會噴錯了,
但如果運氣好測式資料沒注意,但結果噴錯就往往造成程式的中斷就有點難搞了。
好一點的就使用一個檢查的 function 丟進去來處理
function checkData (x) {
// 設立一些條件
return (x === null || x === undefined || x.length === 0) ? false : true
}
let data1 = getData(x)
let checkData1 = checkData(x)
if (checkData1) {
// do data1 作用
}
換個思維
如果我們把「過濾條件」和「噴錯」(new Error)都視為一個 Box 內的資料,
如果可以做值的運算就叫 Right,不可以運算的就叫 Left,
再運用之前介紹的 fmap 的觀念(就是丟一個 function 去處理 Box 內的值),
那其實是不是感覺省事很多,就不用一堆 if-else
。
再回首
再看一下最上面的小工具函數,大概就可以有這樣的示範 code
let data1 = Box(3).fmap(x => x + 1) // Right(4)
let data2 = Box(undefined).fmap(x => x + 1) // Left(null)
// 甚至 error
let data3 = tryCatch(() => null.length).fmap(x => x + 1) //?? Left(e)
let data4 = fromNullable(null).fmap(x => x + '!!') // Left(null)