Tommy Chheng

Icon

All Things Programming!

Partial functions in Scala

I ran into a problem where i wanted to get a formatted string for each tuple in a list but ended on a detour to learning about partial functions. Consider this simple case:

scala> val wordCount = List(("a",1),("b",2))
wordCount: List[(java.lang.String, Int)] = List((a,1), (b,2))

I can just use ._1 and ._2 on each tuple but it isn’t very readable. If someone else is reading the code, will they know what ._1 is for?

scala> wordCount.map(wc => wc._1+":"+wc._2)
res4: List[java.lang.String] = List(a:1, b:2)

We can use case classes to the rescue:

scala> wordCount.map(case(word,count) => word+":"+count)
:1: error: illegal start of simple expression
wordCount.map(case(word,count) => word+":"+count)
^

Opps, as dpp mentioned on the scala mailing list, case is a partial function so this syntax won’t work.

What is a partial function you ask?

Partial functions allow you to specify a subset of the parameters in a function. Imagine this trivial case where you have an add function.

scala> def add(t1:Int,t2:Int) = t1+t2
add: (t1: Int,t2: Int)Int

You can create a partial function addByOne which will have one parameter bounded and another unbounded.

scala> val addByOne = add(1,_:Int)
addByOne: (Int) => Int =<function1>

Then you can evaluate addByOne by specifying the unbounded parameter:

scala> addByOne(10)
res5: Int = 11

case: a Partial Function

Back to our original example why calling map with parentheses doesn’t work.
case is a partial function which will only be called if the isDefinedAt method is true. If it was a Function, it would throw a MatchError when no case matches are found.


scala> val myPartial:PartialFunction[Any,String]= {case(word,count) => word+":"+count}
myPartial: PartialFunction[Any,String] = <function1>

We can apply the partial function to each tuple explicitly:

scala> wordCount.map(x => myPartial(x))
res14: List[String] = List(a:1, b:2)

or with the implied argument:

scala> wordCount.map(myPartial)
res15: List[String] = List(a:1, b:2)

We can also pass the partial function as an anonymous function(or closure):

scala> wordCount.map({case(word,count) => word+":"+count})
res16: List[java.lang.String] = List(a:1, b:2)

and remove the the extra parentheses:

scala> wordCount.map {case(word,count) => word+":"+count}
res17: List[java.lang.String] = List(a:1, b:2)

Thanks to dpp for bringing partial functions to my attention.

Category: Programming, Scala

Tagged:

  • Eddie Watson
    This article seems to conflate partially applied functions and partial functions. They are two distinct concepts with admittedly similar names.
blog comments powered by Disqus

Read Books on the Amazon Kindle 3