iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 18
0

本系列文章,內容以探討 Kyle Simpson. Functional-Light JavaScript 一書內容為主

  • 目標:是讀懂 FP,能用 code 與人交流,而不是被壓在 FP 的術語大山下喘不過氣。
  • 提醒:本文中各種的 FP 小工具,僅為邏輯演示,實際上並不適合在 production 中使用,建議使用 FP library。
  • 原文地址:Functional-Light JavaScript

從左到右

前幾天探討的 compose(..) 邏輯是從右到左,剛好對應到手動合成函數的樣子,

// 右到左
compose(fn2, fn1)
// 內到外
fn2( fn1() )

在 FP 還有另一種合成的方式是從左到右處理,通常在 FP 函數庫稱作 pipe(..)

function pipe(...fns) {
    return function piped(result) {
        var list = fns.slice()

        while (list.length > 0) {
            result = list.shift()(result)
        }
        
        return result
    }
}

// ES6

var pipe = (...fns) =>
    result => {
        var list = fns.slice()
        
        while (list.length > 0) {
            result = list.shift()(result)
        }
        
        return result
    }

小工具 pipe(…)

邏輯上,其實只要把 compose(..) 的參數反轉就能實現,

var pipe = reverseArgs( compose )

重構找長單字範例

compose(skipShortWords, deDuplicate, splitString)

// pipe 版本

pipe(splitString, deDuplicate, skipShortWords)

範例1 compose 與 pipe 對照

使用 pipe(..) 最大的優點是在於以函數的執行順序排列參數,pipe(splitString, deDuplicate, skipShortWords) 閱讀順序就是執行順序,先 splitString 再 deDuplicate 最後再 skipShortWords。

pipe 與 partial application

也順便結合之前所學使用 pipe 與 partial,看看與 compose 的差異,在 Day 16 我們使用 partialRight 將參數順序反轉應用在 compose,

var wordsFilterBy = partialRight(compose, deDuplicate, splitString)

var longWords = wordsFilterBy( skipShortWords )

範例2 找長單字 partialRight compose 應用

對比使用 pipe,不需反轉順序,直接使用 partial

var wordsFilterBy = partial(pipe, splitString, deDuplicate)

關於抽象化

抽象化的概念經常被定義為將通用的部分切出去,在多處使用通用邏輯,修修改改常會有忘記改到的風險,常聽到一個抽象化原則:DRY(don't repeat yourself),就是為了避免這個風險。

關於組合,最大的好處就是做到了 DRY。除了 DRY,我們學習 FP 更高的目標是在分解程式碼成為一塊塊的小積木。

分解後,程式碼的目的更明確,處理複雜性問題的方法就是拆解,分離出關注點。

除了分解,另一個方式就是寫** Declarative** (宣告式,聲明式)風格,對比 Imperative (命令式),聲明式描述輸出的樣子,命令式關心詳細實作。

以 ES6 的 destructing 解構賦值為例:

function getData() {
    return [1,2,3,4,5];
}

// Imperative
var tmp = getData();
var a = tmp[0];
var b = tmp[3];

// Declarative
var [ a ,,, b ] = getData();

範例3 Declarative v.s. Imperative

組合與抽象化

回到函數組合,先比較 Declarative v.s. Imperative 的組合方式:

// Declarative 聲明式
var longWords = compose(skipShortWords, deDuplicate, splitString)

// Imperative 命令式
function longWords(text) {
    return skipShortWords( deDuplicate( splitString( text ) ) );
}

使用函數組合是一種聲明式的抽象,我們關心 what:
一個字串經過會 splitString、deDuplicate、skipShortWords;而把 how: 一步一步得到結果的細節留在 compose 裡做。

對比使用命令式寫

var wordsArray = splitString( loremString )

var wordsUsed = deDuplicate( wordsArray )

skipShortWords(wordsUsed)

函數組合不是只有 DRY,更能夠把關注點放在 What,提升程式可讀性。

小結:

pipe(..) 是函數組合的另一種方式,讓參數閱讀順序就是執行順序,

而函數組合更是強大的抽象化的工具,能夠把命令式的how 抽象為可讀性更好的聲明式,讓程式專注在 what


上一篇
Good Morning, Functional JS (Day 16, Composition part 2)
下一篇
Good Morning, Functional JS (Day 18, Ramda 實作)
系列文
Good Morning, JS functional Programing.31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言