0

I'm trying to learn Scala and I'm confused when to use currying functions over partially applied functions.

I'm pretty sure those concepts were not made to be redundant, but I can't see the real purpose of the different approaches.

I was experimenting with this code:

Solution uses currying:

object CurryTest extends App {

  def filter(xs: List[Int], p: Int => Boolean): List[Int] =
    if (xs.isEmpty) xs
    else if (p(xs.head)) xs.head :: filter(xs.tail, p)
    else filter(xs.tail, p)

  def modN(n: Int)(x: Int) = ((x % n) == 0)

  val nums = List(1, 2, 3, 4, 5, 6, 7, 8)

  println(filter(nums, modN(2)))
  println(filter(nums, modN(3)))
}

Solution uses partially applied function:

object PartialFunctionTest extends App {

  def filter(xs: List[Int], p: Int => Boolean): List[Int] =
    if (xs.isEmpty) xs
    else if (p(xs.head)) xs.head :: filter(xs.tail, p)
    else filter(xs.tail, p)

  def modN(n: Int, x: Int) = ((x % n) == 0)

  val nums = List(1, 2, 3, 4, 5, 6, 7, 8)

  println(filter(nums, modN(2,_)))
  println(filter(nums, modN(3,_)))
}

Both give the same result:

List(2, 4, 6, 8)
List(3, 6)

Are those different approaches equivalent?

Could somebody enlighten me, what is the best use case for each?

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
elaspog
  • 1,635
  • 3
  • 21
  • 51
  • 2
    Terminology nitpick: `modN(2, _)` is not a partial function. It's a *partially applied function*, which, despite the similar name, is not the same thing. See http://sandrasi-sw.blogspot.com/2012/03/understanding-scalas-partially-applied.html – Ian McLaird Sep 21 '17 at 15:50

1 Answers1

2

This is a mostly stylistic choice. In your case it makes no real difference but I would argue that the first alternative with the multiple parameter lists looks nicer.

In terms of style, use a curry function when the method is meant to be used in a curried way. Use partial application when you need to curry a function that was not designed for it.

There are some cases where you must use one over the other however. For example, multiple parameter lists allows you to use var args in both lists

def ex1(ints: Int*)(strings: String*)

If you have generic types, these types can flow from one list to the next

def ex2[A](input: A)(func: A => A): A = func(input)
ex2(1)(x => "Hello " + x) //compile error, A is Int from first parameter list

def ex2a[A](input: A, func: A => A): A = func(input)
ex2a(1, x => "Hello " + x) //compiles and returns "Hello 1",  A is Any because all parameters in the same list are used to determine it's type

There are a couple cases where you might need to use partial application. For example in a case class constructor, if you try to curry it only the first parameter list becomes part of the case class fields.

case class ex3(a: Int, b:Int)
ex3(1, 2) == ex3(1,3) //false

val y: Int => ex3 = ex3(1,_)

y(2) == y(2) //true
y(2) == y(3) //false

case class ex3a(a: Int)(b:Int)

ex3a(1)(2) == ex3a(1)(3) //true

val x: Int => ex3a = ex3a(1)

x(1) == x(1) //true
x(1) == x(2) //true
Jeffrey Chung
  • 19,319
  • 8
  • 34
  • 54
puhlen
  • 8,400
  • 1
  • 16
  • 31
  • 1
    Currying and partially applied functions are not one of the same, and are not only a style choice. A curried function is one which for an N-arity produces N * 1 - arity functions, meaning a function taking a single argument, producing additional functions of 1 arity. A partially applied function produces one new function, with n - m the number of arguments. – Yuval Itzchakov Sep 21 '17 at 16:47