Sunday, June 7, 2009

Function.tupled, Function.curried

Scala's library hides small gems of which little is said. Let me speak of a personal favorite here, the object Function.

This object has a number of methods to construct a function from another function (or others, in the case of "chain").  They are variations of "curried", "tupled", "uncurried" and "untupled". As their names makes clear, they transform a function whose arguments are not curried or are not a tuple into a function whose arguments are curried, or which receives a tuple as an argument. Take, for instance, this definition of zipMap, which applies a function of two arguments between two lists:



scala> def zipMap[A,B,C](l1 : Seq[A], l2 : Seq[B])(f : (A,B) => C) =
| l1 zip l2 map (x => f(x._1, x._2))
zipMap: [A,B,C](l1: Seq[A],l2: Seq[B])(f: (A, B) => C)Sequence[C]

scala> zipMap (List(1, 3, 9), List(5, 2, 3)) ((x,y) => x max y)
res5: Sequence[Int] = List(5, 3, 9)


This is not a bad definition, but accessing the tuple arguments to pass to f is too much mechanics for my taste. So, instead, we could do this:



scala> def zipMap[A,B,C](l1 : Seq[A], l2 : Seq[B])(f : (A,B) => C) =
| l1 zip l2 map Function.tupled(f)
zipMap: [A,B,C](l1: Seq[A],l2: Seq[B])(f: (A, B) => C)Sequence[C]

scala> zipMap (List(1, 3, 9), List(5, 2, 3)) ((x,y) => x max y)
res4: Sequence[Int] = List(5, 3, 9)


Much better, don't you think? By the way, this was done on 2.8, where Seq has the method zip.

4 comments:

  1. Wow, that was usefull!
    Thx!

    ReplyDelete
  2. Thanks, I just looked for a way to feed my 3 arg function with tuples. It turns out with Scala 2.8 you can just use:

    func.tupled

    ReplyDelete
  3. Too complex example.

    ReplyDelete
  4. Another sample

    Given a list of tuples
    scala> val l = List(("A",10),("B",20))
    l: List[(java.lang.String, Int)] = List((A,10), (B,20))

    to create instances of a case class
    scala> case class Person(name:String, age:Int)
    defined class Person

    Now instead of
    scala> val p = l map {case (n,a) => Person(n,a)}
    p: List[Person] = List(Person(A,10), Person(B,20))

    You can also write
    scala> val p = l map Person.tupled
    p: List[Person] = List(Person(A,10), Person(B,20))

    ReplyDelete