Apr 23, 2010 View Comments
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.