- Concepts of Programming Languages

Option Type

Instructor:

Reporting Missing Data

  1. def find[X](xs: List[X],
  2. p: X=>Boolean) : X =
  3. xs match {
  4. case Nil => ???
  5. case x :: rest =>
  6. if p(x) then x
  7. else find(rest, p)
  8. } ensuring {
  9. case result => ???
  10. }
  1. def reduce[X](xs: List[X],
  2. f: (X,X)=>X) : X =
  3. xs match {
  4. case Nil => ???
  5. case x :: Nil => x
  6. case x :: rest =>
  7. f(x, reduce(rest))
  8. } ensuring {
  9. case result => ???
  10. }
  • case Nil => null: contract and clients have to distinguish between null and a result
  • case Nil => throw IllegalArgumentException(...): exceptions for expected behavior bad, how to write exception contract?

Learning Objectives

How to represent missing results?

  • Identify uses of option types

Option Type

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

Absence of Data

Programming with null

  1. def find[X](xs: List[X],
  2. p: X => Boolean): X =
  3. xs match
  4. case Nil => null.asInstanceOf[X]
  5. case x :: _ if p(x) => x
  6. case _ :: rest => find(rest, p)
  7. end find

Programming with optionals

  1. def find[X](xs: List[X],
  2. p: X => Boolean): Option[X] =
  3. xs match
  4. case Nil => None
  5. case x :: _ if p(x) => Some(x)
  6. case _ :: rest. => find(rest, p)
  7. end find

Absence of Data: Clients

  • Find x>5, if found return "found x", else find x>3, if found return "found x", else print "not found"
  1. val xs = List(1, 2, 3, 4, 5)

Client handling null

  1. find(xs, _ > 5) match
  2. case null => find(xs, _ > 3) match
  3. case null => "not found"
  4. case n => s"found $n"
  5. end match
  6. case n => s"found $n"
  7. end match

Client handling option

  1. find(xs, _ > 5) orElse find(xs, _ > 3) match
  2. case None => "not found"
  3. case Some(n) => s"found $n"
  4. end match
  • null: no compiler support for null-checking, cannot have methods
  • Map, fold, filter all work on Option

Nested Options

Safe division returns on zero: None (division undefined)

  1. def safeDivide (n:Int,m:Int) : Option[Int] =
  2. if m == 0 then None
  3. else Some (n/m)
  1. def safeDivide (n:Int,m:Int) : Option[Int] =
  2. Option.when(m!=0)(n/m) // Option.unless(m==0)(n/m)
  • Divide an optional int safely
    1. val i: Option[Int] = Some(4)
    2. i.map(safeDivide(_,2)) // val res: Option[Option[Int]] = Some(Some(2))
    3. i.map(safeDivide(_,0)) // val res: Option[Option[Int]] = Some(None)
  • Avoid nested options
    1. val i: Option[Int] = Some(4)
    2. i.flatMap(safeDivide(_,2)) // val res: Option[Int] = Some(2)

Types of Emptiness

Scala

  1. def sum (xs : List[Int]) : Int = xs match
  2. case Nil => 0
  3. case y::ys => y + sum(ys)
  • Nil: empty list
  • null: empty reference
  • None: empty option
  • Unit: always has ()

Java

  1. int sum (Node<Integer> xs)
  2. if (xs == null) return 0;
  3. else return xs.item + sum(xs.next);
  • null: used to represent emptiness
  • null: empty reference
  • Optional.empty(): empty option
  • void: no return value

Nullable Types

  • In Scala, we often pretend null does not exist
  • Recent languages identify None and null: e.g. Swift, Kotlin
  • These languages distinguish nullable and non-nullable types

Nullable Types

Kotlin nullable versus non-nullable types

  • T? in Kotlin resembles Option[T] in Scala
  • null is used for None
  • Types without ? do not allow null
  1. var a: String = "abc"
  2. a = null /* compilation error */
  3. a.length /* always safe */
  4. var b: String? = "abc"
  5. b = null /* ok */
  6. b.length /* compiler error */
  7. b!!.length /* may raise exception */
  8. /* Safe call */
  9. b?.length
  10. /* Explicitly expanded safe call */
  11. if (b != null) b.length else null
  12. /* Safe call with default */
  13. b?.length ?: -1
  14. /* Explicitly expanded */
  15. val t = b?.length; if (t != null) t else -1

Summary

  • Option type represents presence/absence of data
  • Either type represents alternative results
  • Support higher-order functions map, etc.
  • Enable expressing alternative computation attempts concisely (orElse, getOrElse)