CSC447

Concepts of Programming Languages

Option Type

Instructor: James Riely

Option Type

  • Option[T] resembles List[T] with length ≤ 1
  • Example expressions of type Option[Int]
    
    None 
                  
    
    Some (5)
                  
  • Principled approach to missing data

Programming with Exceptions


def getDirs1 (dirName : String) : List[java.io.File] = 
  val dir = new java.io.File (dirName)
  val xs = dir.listFiles
  if xs == null then throw new java.io.FileNotFoundException
  xs.toList.filter (_.isDirectory)
          

getDirs1 ("/tmp")
            

res1: List[java.io.File] = List(/tmp/log, /tmp/cache)
            

getDirs1 ("/temp")
            

java.io.FileNotFoundException
            

Programming with Optionals


def getDirs2 (dirName : String) : Option[List[java.io.File]] = 
  val dir = new java.io.File (dirName)
  val xs = dir.listFiles
  if xs == null then return None
  Some(xs.toList.filter (_.isDirectory))
          

getDirs2 ("/tmp")
            

res2: Option[List[java.io.File]] = Some(List(/tmp/log, /tmp/cache))
            

getDirs ("/temp")
            

res3: Option[List[java.io.File]] = None
            

Client with Exceptions


def printNumTemp1 () =   
  var result : List[java.io.File] = Nil
  var found = false
  for s <- List("/temp", "/tmp"); if !found do 
    try 
      result = getDirs1(s)
      found = true
    catch
      case e: java.io.FileNotFoundException => ()
  found match 
    case false => println("No Temporary Directory.")
    case true  => println(result.length)
          

Client with Options


def printNumTemp2 () = 
  getDirs2("/temp") orElse getDirs2("/tmp") match 
    case None => println("No Temporary Directory.")
    case Some(result) => println(result.length)
          
Map, fold, filter all work on Option

Option vs null

  • An option is a type that may have something or nothing
  • Scala has many values that represent nothing
    • None: empty option
    • Nil: empty list
    • null: reference to nothing
  • Unit is not an option type
    • Unit always has nothing
    • Unit nothing is ()

Nil

Scala:

def sum (xs : List[Int]) : Int = xs match 
  case Nil => 0
  case y::ys => y + sum(ys)
          

null

Java:

int sum (Node<Integer> xs) 
  if (xs == null) return 0;
  else return xs.head + sum(xs.tail);
          

Nullable types

  • In Scala, we often pretend null does not exist
  • Can be made explicit using -Yexplicit-nulls

var a: String = "abc"
a = null /* compilation error */
a.length /* always safe */

var b: String|Null = "abc"
b = null /* ok */
b.length /* compiler error */

b.nn.length  /* may give Null Pointer Exception */

if b != null then b.nn.length else null /* Safe call with result Int|Null */
if b != null then b.nn.length else -1   /* Safe call with result Int      */
          

Kotlin nullable versus non-nullable types

  • Kotlin and Swift distinguish nullable and non-nullable types.
  • T? means T|Null
  • Types without ? do not allow null

var a: String = "abc"
a = null /* compilation error */
a.length /* always safe */

var b: String? = "abc"
b = null /* ok */
b.length /* compiler error */

b!!.length /* may give Null Pointer Exception */

b?.length                         /* Safe call with result Int? */
if (b != null) b.length else null /* expanded */

b?.length ?: -1                   /* Safe call with result Int  */
val t = b?.length; if (t != null) t else -1   /* expanded */
          

Java optional

Java 8 added java.util.Optional

The intended use is narrow:

Optional is intended to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result," and using null for such was overwhelmingly likely to cause errors.

Map v FlatMap


def map[X,Y](maybe_x:Option[X], f:X=>Option[Y]) : Option[Option[Y]] =
  maybe_x match 
    case None    => None
    case Some(x) => Some(f(x))

def flatMap[X,Y](maybe_x:Option[X], f:X=>Option[Y]) : Option[Y] =
  maybe_x match 
    case None    => None
    case Some(x) => 
      val result : Option[Y] = f(x)
      result match 
        case None => None
        case Some (y) => Some(y)
          

Map v FlatMap


def index [X] (xs:List[X], n:Int) : Option[X] = 
  xs match 
    case Nil             => None
    case y::_  if n == 0 => Some (y)
    case _::ys           => index (ys, n - 1)

def safeDivide (n:Int,m:Int) : Option[Int] = 
  if m == 0 then None
  else Some (n/m)

def as = List(11,21,31)
          

scala> map(index(as,2), safeDivide(_,2))
val res10: Option[Option[Int]] = Some(Some(15))

scala> flatMap(index(as,2), safeDivide(_,2))
val res11: Option[Int] = Some(15)
          

Map v FlatMap


def index [X] (xs:List[X], n:Int) : Option[X] = 
  xs match 
    case Nil             => None
    case y::_  if n == 0 => Some (y)
    case _::ys           => index (ys, n - 1)

def safeDivide (n:Int,m:Int) : Option[Int] = 
  if m == 0 then None
  else Some (n/m)

def as = List(11,21,31)
          

scala> for
  a <- index(as,2)
  b <- safeDivide(a,2)
yield b
val res11: Option[Int] = Some(15)