Tuesday, 23 August 2016

Scala's Option/Some/None Type In Depth (Part-2)

Post Brief Table of Content

  • Introduction
  • Option is a Container
  • Scala's Collection Functions on Option Type
  • Scala’s Option Type In Pattern Matching
  • Real-time Scenarios of Option
  • How null works with Option Type
  • Option fold Function
  • Option Misc Concepts

Introduction

Before reading this post, please go through my previous post at: Scala Option Type Basics

I have already discuss some basic concepts about Scala Option Type in my previous post.
In this post, I’m going to discuss some more Advanced concepts and some Real-Time usage of Scala Option Type.

Option is a Container

In Scala, Option is a Container. Even though it looks like a Scala Collection, but not a Collection.

It contains either one element or zero element that means it can have at most one element. If it contains one element that is Some type. If it does NOT have any value, that is None type.

NOTE:- When I say Option type means it also includes it's sub-types: Some and None.

Scala’s Collection functions on Option Type

In Scala Language, Option type is not a Collection class. However, it supports most of the Scala's Collection API Functions.

Like Collection API or Monad, Option type supports map, flatMap, filter, foreach etc. useful functions.

NOTE:- If you are new to Monad, I'm going to write a post on it soon. Will add a link here once I finish that post. Mean while, Monad is one of the Functional Design Patterns available in Scala Language to solve some problems.
Example-1:-
Scala map function on Option Type
scala> val empNo:Option[String] = Some("1234")
empNo: Option[String] = Some(1234)

scala> empNo
res0: Option[String] = Some(1234)

scala> empNo.map(no => no.toInt)
res1: Option[Int] = Some(1234)

scala> empNo
res2: Option[String] = Some(1234)
Here if we observe that we have mapped from Option[String] to Option[Int] using map function. Just like Collection API, we can use map function on Option type.

Example-2:-
Scala foreach function on Option Type
scala> val empNo:Option[String] = Some("1234")
empNo: Option[String] = Some(1234)

scala> empNo.foreach(println)
1234

NOTE:- If you are new to map, flatMap, filter, foreach etc functions, please refer my previous posts. I have dedicated one post for each function to explain them in detail.

Scala's Option Type In Pattern Matching

One of the advantages of Scala's Option type is that it is easy to use in Pattern Matching.
Example-1:-
Write a Pattern Matching on Some type

scala> val empNo:Option[String] = Some("1234")
empNo: Option[String] = Some(1234)

scala> empNo match {
     |  case Some(no) => no.toInt
     |  case _        => -1
     | }
res12: Int = 1234
Example-2:-
Write a Pattern Matching on None type
scala> val empNo:Option[String] = None
empNo: Option[String] = None

scala> val empNo2:Option[String] = None
empNo2: Option[String] = None

scala> empNo2 match {
     |  case Some(no) => no.toInt
     |  case _        => -1
     | }
res13: Int = -1
We will discuss on how to use Option type and write Pattern Matching on Option type in next section. 

Real-time Scenarios of Option

In this section, I will discuss couple of real-time usages of Option type in Scala-based Projects.

Scenario-1:-
Pattern Matching on Play Form Input Field data

Let us assume that we have a Play View form with some set of fields. We want to do something on these fields in Play Controller. For simplicity, I'm going to use only one field.

When we click on submit on Play Form, it sends a request object with all form data to a Controller. In Controller, we need to retrieve that field data and do something. That field may or may not a value.

We know that if we want to deal with "May or may not a value" scenario, we need to use Option type to that field.

val empNo = request.data.get(EMP_NO_KEY) match {
  case Some(no) => no
  case None     => -1
}

Scenario-2:-
Here some useful code snippet from HRMS module
class EmpService{

  def getEmpDetailsById(empID: Long):Option[Employee] = { 
      EmpDAO.getEmpDetailsById(empID)
  }

}

object Controller.. {

  def getEmpName:String = ... {
    val emp = EmpService.getEmpDetailsById(empID)

   val empName = emp match {
      case Some(e)  => e.empname
      case None     => "Unknown"
    }

    empName
  }

}
Here I have provided only some pseudo code. My main intention of this example is that how to use Option in some real-time Scala/Play projects. 
NOTE:- If you are new to Play Framework, Please go through my Play Framework tutorial.

How null works with Option Type

If we try to assign null value to a Option type, we will get some unexpected results as shown below.


Example:-
scala> val ename:Option[String] = Option(null)
ename: Option[String] = None

scala> ename.get
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 33 elided

scala> val ename:Option[String] = Some(null)
ename: Option[String] = Some(null)

scala> ename.get
res5: String = null

Option fold Function
Just like Pattern Matching, Option also have one function: fold to perform Some/None Pattern.

Example-1:- 
Non-Empty Option that means it is Some type

scala> val empName:Option[String] = Some("Rams")
empName: Option[String] = Some(Rams)

scala> empName.fold("Unknown") {_.toUpperCase}
res4: String = RAMS

As empName is non-empty Option type, it uses toUpperCase function to convert name to upper case letters.

Example-2:- 
Empty Option that means its None type

scala> val empName:Option[String] = None
empName: Option[String] = None

scala> empName.fold("Unknown") {_.toUpperCase}
res5: String = Unknown
As empName is empty Option type, it uses default value "Unknown" and control does NOT execute toUpperCase function. That's why default or alternative value is not converted to upper case letters.

NOTE:-
  • To understand this fold function in detail, please go through my "Scala Folding Functions In Depth" post. (Will add that link here soon)
Option Type Misc Concepts

Here we will discuss some miscellaneous concepts, yet import to know them.
  • As we know, We can assign Some or None to Option Type 
  • We cannot assign None to Some type
scala> val a:Some[String] = None
<console>:10: error: type mismatch;
 found   : None.type
 required: Some[String]
       val a:Some[String] = None
  • What is the type of None?
None is a case object. In Scala, like any object case object also have type attribute to indicate its type.

Example:-
scala> val x = None
x: None.type = None

scala> x
res14: None.type = None
Here None.type is the type of None case object. In the same way each object a type attribute to indicate it's type.

NOTE:- Scala Objects: Companion Object, Singleton Object, Case Object

Please see my my posts to understand these concepts like Companion Object. I have dedicated one post to explain these concepts in detail.

That’s it all about “Scala Option” type advanced concepts. We will discuss some more important Scala concepts in my coming posts.
Please drop me a comment if you like my post or have any issues/suggestions. I love your valuable comments so much.
Thank you.