On Currying and Partial Function Application

CurrryingPartial Function Application là hai khái niệm khá thú vị trong Functional Programming, để hiểu được bản chất của chúng không phải khó, nhưng quan trọng là lợi ích và khi nào chúng ta nên dùng. Hôm nay mình sẽ cùng các bạn đi tìm hiểu qua các ví dụ cụ thể.

Currying

Hiểu một cách đơn giản thì Currying là quá trình chuyển đổi một Function có nhiều tham số thành một chuỗi các Function mà ở đó mỗi Function chỉ có duy nhất một tham số.
Ví dụ:
Thay vì cách viết thông thường

def sum(a: Int, b: Int) = a + b
val total = sum(2, 8) //> 10

chúng ta có thể viết dưới dạng

def sum(a: Int)(b: Int) = a + b
val total = sum(2)( 8) //> 10

tương đương với cách viết:

def sum(a: Int): (Int => Int) = {
  (b: Int) => {
    a + b
  }
}

tổng quát hoá, với một Currying function có nhiều tham số:

def f(args1)...(args100)(args101) = E

ta có thể hiểu function trên tương đương với:

def f(args1 => (args2 => ...(args101 => E)...))

Trong Scala cũng cho phép biến đổi từ Function thông thường sang Currying function:

val sum = (a: Int, b: Int) => a + b
val sumCurried = sum.curried //> sumCurried: Int => (Int => Int) = <function1>

Partially applied functions

Là khi tại lời gọi hàm, chúng ta không cần truyền vào đầy đủ các tham số mà hàm định nghĩa, mà có thể để trống một vài tham số, lúc này lời gọi hàm sẽ trả về một function mới với số lượng tham số là những tham số chúng ta đã để trống ở lời gọi hàm trên.
Ví dụ chúng ta có function sum:

def sum(a: Int, b: Int) = a + b

Khi muốn tính tổng của 2 với 5 và 2 với 10, thay vì chúng ta gọi function sum(2, 5) và sum(2, 10), chúng ta có thể làm theo cách sau:

val f = sum(2, _: Int)
val twoPlusFive = f(5) //> 7
val twoPlusTen = f(10) //> 12 

Khi nào nên sử dụng Currying và Partially applied functions?

Giả sử chúng ta cần gửi một message đến danh sách EndPoint, chúng ta có thể xây dựng chương trình:

case class Message(value: String)

case class EndPoint(prompt: String) {
  def send(m: Message) {
    println(this.prompt + " " + m.value)
  }
}

def route(message: Message)(e: EndPoint) = e.send(message)

Bây giờ chúng ta muốn send một message “Hello” đến nhiều EndPoint:

val routeHello = route(Message("Hello"))
routeHello(EndPoint("Sending")) //> Sending Hello
routeHello(EndPoint("Sending over again")) //> sending over again Hello

Hãy cùng xem một ví dụ khác, chúng ta sẽ xây dựng chương trình filter lấy ra các số chẵn và các số lẻ của một danh sách:

“`
def filterBy(f: Int => Boolean)(xs: List[Int]) = {xs filter f}

def even(x: Int) = x % 2 == 0

def odd(x: Int) = x % 2 == 1

val xs = List(1, 2, 3, 5, 6, 7, 9, 8) //> xs: List[Int] = List(1, 2, 3, 5, 6, 7, 9, 8)

val eventFilter = filterBy(even) _

Add a Comment

Scroll Up