Partial Functions in Scala – A Comprehensive Guide


1. Objective

This tutorial on Partial functions in Scala will help in learning the different types of functions in Scala, basics of scala partial functions and different ways to define it while doing Scala programming. These Scala functions would help programmers to do programming in Scala in more effective manner.

So are you all ready to learn Scala functions?

Let us brush our Scala concepts and Scala features before going ahead.

Partial Functions in Scala

2. Introduction to Partial Functions in Scala

Most of the functions that we study fall under the category of Total function which means the

function properly supports every possible value that meets the type of the input parameters. A simple function like def double(x: Int) = x*2 can be considered a total function as there is no input x that the double() function could not process.

However there are some functions that do not support every possible value that meets the input type. For example, a function that returns the square root of the input number will not work if the input number is negative. Similarly a division function with 0 in denominator isn’t applicable. Such functions are called partial functions because they can only partially apply to their input data.

Although this particular situation can be handled by catching and throwing an exception, Scala programming lets you define the divide function as a Partial Function. When doing so, you can explicitly state that the function is defined when the input parameter is not zero.

Example:

val divide = new PartialFunction[Int, Int] {
def apply(x: Int) = 42 / x
def isDefinedAt(x: Int) = x != 0
} 

This approach provides you with many Scala function flexibilities. One thing you can do is test the function before you attempt to use it:

scala > divide.isDefinedAt(1)
res0: Boolean = true
scala > if (divide.isDefinedAt(1)) divide(1)
res1: AnyVal = 42
scala > divide.isDefinedAt(0)
res2: Boolean = false

3. Ways to define Scala Partial Functions

Scala differs with java in many ways. There are many features that creates difference between Scala and Java. Partial Functions in Scala can be defined in different ways as below using Scala control structures:

a. Using case statements

You can define partial function as case statements in Scala. Let us understand this with example.

Example:

val divide2: PartialFunction[Int, Int] = {
case d: Int if d != 0 => 42 / d
}

b. Using Else, orElse

Do you know what is the best part of partial function in Scala programming?

A terrific feature of partial functions is that you can chain them together. For Example – one method may only work with odd numbers and another method may only work with even numbers. Together they can be used to solve all integer problems.

In below mentioned example, two functions are defined that can each handle a small number of Int inputs and convert them to String results:

// converts 1 to “one”, etc., up to 5

val convert1to5 = new PartialFunction[Int, String] {
val nums = Array("one", "two", "three", "four", "five")
def apply(i: Int) = nums(i-1)
def isDefinedAt(i: Int) = i > 0 && i < 6
}

// converts 6 to “six”, etc., up to 10

val convert6to10 = new PartialFunction[Int, String] {
val nums = Array("six", "seven", "eight", "nine", "ten")
def apply(i: Int) = nums(i-6)
def isDefinedAt(i: Int) = i > 5 && i < 11
} 

Taken separately, they can each handle only five numbers. But combined with orElse, they can handle ten numbers as shown below:

scala > val handle1to10 = convert1to5 orElse convert6to10
handle1to10: PartialFunction[Int,String] = < function1 >

It can be used at shown below:

scala > handle1to10(3)
res0: String = three
scala > handle1to10(8)
res1: String = eight

c. Using Collect method

You can run partial functions with the collect method on collections’ classes. The collect method takes a partial function as input, and builds a new collection by applying a partial function to all elements of this list on which the function is defined.

Example:

The divide function shown earlier is a partial function that is not defined at the Int value zero. Here’s that function again:

val divide: PartialFunction[Int, Int] = {
case d: Int if d != 0 = > 42 / d
}

However, if you use the same function with the collect method, it works fine:

scala > List(0,1,2) collect { divide }
res0: List[Int] = List(42, 21)

This is because the collect method is written to test the isDefinedAt method for the elements given to it. So it doesn’t run the divide algorithm when the input value is 0 (but does run it for every other element).

Example:

The first example shows how to create a list of even numbers by defining a PartialFunction named isEven and using that function with the collect method:

scala> val sample = 1 to 5
sample: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
scala> val isEven: PartialFunction[Int, String] = {
case x if x % 2 == 0 => x + " is even"
}
isEven: PartialFunction[Int,String] = <function1>
scala> val evenNumbers = sample collect isEven
evenNumbers: scala.collection.immutable.IndexedSeq[String] =
Vector(2 is even, 4 is even)

Similarly, an isOdd function can be defined and orElse can be used to join these 2 functions to work with the map method:

scala> val isOdd: PartialFunction[Int, String] = {
case x if x % 2 == 1 => x + " is odd"
}
isOdd: PartialFunction[Int,String] = <function1>
scala> val numbers = sample map (isEven orElse isOdd)
numbers: scala.collection.immutable.IndexedSeq[String] =
Vector(1 is odd, 2 is even, 3 is odd, 4 is even, 5 is odd)

For further learning, refer Best Scala books.

Source:

http://www.scala-lang.org/

Leave a comment

Your email address will not be published. Required fields are marked *