• Ruby 2.6 的新功能

    Ruby 2.6 除了主打的 JIT 之外,還引進了一些有趣的新功能。其中有一些可以讓 Ruby 在函數式風格的寫法上更加自然流暢。想來稍微展示一下這些功能的用法。

    1. Compose operator:
    Proc.>>Proc.<<

    雖然 Ruby 天生在某些用法上就採用了函數式風格(例如沒有 for 迴圈,而是用 each + block),但由於高階函式接受的是 block 而非 lambda,lambda 本身也無法簡單組合起來。所以一直以來匿名函式在 Ruby 中除了拿來當參數傳遞之外,用法相當受限。

    在正統的函數式編程中,用 function composition 的方式將多個小函式組合在一起是很基本的操作,舉個之前的例子,假設你想要這樣的連續調用:

    # 注意,因為假設這些函數都是 lambda,所以要用 `.()` 調用
    request = generate_request.()
    response = get_response.(request)
    body = parse_body.(response)
    html = render.(body)
    

    其實除了 html 這個最終結果外,我們不需要中間的臨時變數,而可以改寫成這樣(然後被同事記恨):

    html = render.(parse_body.(get_response.(generate_request.())))
    

    Ruby 2.6 開始你可以用 << (compose) 或是 >> (pipe) 來組合 Proc (或 lambda)。假設我們有兩個 lambda fg,這兩個運算子的作用如下:

    (f >> g).(x)
    # 等同於
    g(f(x))
    
    
    (f << g).(x)
    # 等同於
    f(g(x))
    

    我自己的記憶方式是把這兩個 operator 看成函式呼叫流程的箭頭。f >> g 就是先調用 f ,再將結果傳進 g。反之 f << g 則是先調用 g 再傳給 f

    那麼之前的例子就可以改成下列兩者之一:

    get_html = generate_request >> get_response >> parse_body >> render
    html = get_html.()
    
    # 或是
    
    get_html = render << parse_body << get_response << generate_request
    html = get_html.()
    

    Ruby 2.6 的文件範例如下:

    f = proc {|x| x * x}
    g = proc {|x| x + x}
    
    (f << g).call(2) # => 16
    (f >> g).call(2) # => 8
    

    再進階一點配合 Ruby 原本就有的 Object#methodMethod#curry,想來可以組合出非常有意思的寫法。

    2. Enumerable#filter

    函數式編程最著名的三個高階函式就屬 mapreducefilter 了。但在 Ruby 中,filter 這個高階函式被改名為 select,這一版加上了 filter 的別名,跟其它語言的慣用法一致,再也不會等到測試爆掉才想起函式名稱不一樣了。

    [1, 2, 3, 4].filter {|i| i % 2 == 0} # => [2, 4]
    

    3. Endless range

    現在範圍運算子 .. 可以不加結束的參數:

    [:a, :b, :c, :d][2..] # => [:b, :c, :d]
    
    
    (:b..).lazy.zip(10..).first(3) # => [[:b, 10], [:c, 11], [:d, 12]]
    

    4. Array#union and Array#difference

    Array#union 是聯集,Array#difference 是差集。直接上範例:

    [1].union([2, 3], [1, 2, 4, 5]) # => [1, 2, 3, 4, 5]
    
    
    [1, 1, 2, 2, 3, 3, 4, 5].difference([1, 2, 4]) # => [3, 3, 5]
    

    結語

    除了這些之外,Proc.call 調用快了大約 1.4 倍。另外還有 Object#thenHash#mergeEnumerable#to_h 等等,就找機會試玩看看吧。Happy hacking!

subscribe via RSS