Long Hoang

People often call me Tobi, but you can call me tonight!

Navigation
 » Home
 » About Me
 » Github
 » XML Feed

Ruby 2.6 Function Composition

03 Jun 2019 » ruby, functional

Phiên bản ruby 2.6 được released vào giáng sinh năm ngoái. Sau khi đọc lướt qua thì mình thấy tính năng mới function composition có chút thú vị giống kĩ thuật trong các ngôn ngữ functional programming

Function composition là gì ?

Hiểu một cách đơn giản là tạo ra một function mới dựa trên sự kết hợp của những functions đã có.

  • Function 1 (Input Type A -> Output Type B)
  • Function 2 (Input Type B -> Output Type C)
  • Function 3 = Function 1 » Function 2 (Input Type A -> Output Type C)

Function 3 thực thi được là do kiểu giá trị trả về của Function 1 tương ứng với kiểu giá trị đầu vào của Function 2.

Function composition trong Ruby

Đại loại nó sẽ như thế này:

class Proc
  def << block
    proc { |*args| self.call( block.to_proc.call(*args) ) }
  end
end

Bạn có thể xem cách implement cụ thể tại đây

Chúng ta có một ví dụ đơn giản như sau:

DAILY_WORKING_HOURS = 8
HOURLY_WAGE_IN_CENTS = 10 * 100
TAX_RATE = 0.25

sub_pay       = ->(days)   { (days * DAILY_WORKING_HOURS) * HOURLY_WAGE_IN_CENTS }
pay_tax       = ->(salary) { salary * (1 - TAX_RATE) }
format_salary = ->(salary) { "$#{format('%0.2f', salary / 100.0)}" }

Chúng ta sử dụng toán tử >> để kết hợp những proc trên để tính lương dựa trên số ngày làm việc rồi sau đó trừ thuế và cuối cùng là format về dollar cho dễ đọc hơn.

after_tax_pay = sub_pay >> pay_tax >> format_salary
puts after_tax_pay.call(22)
# $1320.00

Tính năng này theo bản thân thấy cũng khá là hay khi học hỏi từ những ngôn ngữ functional programming khác, tuy nhiên cũng sẽ tồn tại một số khả năng gây ra bug như việc kiểm soát trật tự kết hợp các function không đúng thứ tự. Và còn nữa việc types match không chính xác giữa output của function trước và input của function sau cũng sẽ gây ra lỗi như sau khi ta format_salary (trả về kiểu string) trước rồi sau đó tính pay_tax (yêu cầu kiểu số)

after_tax_pay = sub_pay >> format_salary >>> pay_tax
# nil

Ruby 2.7 đang có lộ trình phát triển typechecker, hy vọng sẽ có gì đó bổ sung để giải quyết vấn đề types match ở trên.

References:

  • https://www.ruby-lang.org/en/news/2018/12/25/ruby-2-6-0-released/
  • https://bugs.ruby-lang.org/issues/6284
  • https://github.com/ruby/ruby/blob/trunk/proc.c#L3137