introduction to option monad in scala

75
Some(slides) val reasonsToUseNull = None Friday, July 19, 13

Upload: jan-krag

Post on 10-May-2015

1.809 views

Category:

Technology


1 download

DESCRIPTION

Presentation on handling non-existence of data in Java et. al. (e.g. the problem with pesky nulls) and an introduction to the Option monad in Scala as a "solution" to this problem. I presented this talk June, 28th 2013 at CPH Scala Group meeting, and a week later, July 3rd, at the "Scala User Group Århus" meetup. In this short introduction, I try to frame the problem, i.e. the large amounts of error-prone null-checking code we usually have to write in Java, and Introduce the Option monad (Some/None) in Scala, as a solution. I explain the basics of what the Option class provides, and various ways of using it, ranging from basic level isEmtpy, over pattern-matching to more advanced fully functional "collection-style" (e.g. map, flatMap) operations and finally by using the for-comprehension. Also includes links to relevant resources for further reading on the last slide.

TRANSCRIPT

Page 1: Introduction to Option monad in Scala

Some(slides)val reasonsToUseNull = None

Friday, July 19, 13

Page 2: Introduction to Option monad in Scala

Who am I?

Java (& Scala) Developer at Schantz A/S

Polyglot curious, Coursera junkie

Interested in HCI and Usability

https://github.com/JKrag

@jankrag

• Geek, builder and flyer of kites, reptile & cat breeder, Rubik's puzzle fan

Friday, July 19, 13

Page 3: Introduction to Option monad in Scala

Oh we wish...

val customer = Customers.findById(1234)customer.getAccount(FUNSTUFF).getLastInterest.getAmount

Friday, July 19, 13

Page 4: Introduction to Option monad in Scala

Oh we wish...

val customer = Customers.findById(1234)customer.getAccount(FUNSTUFF).getLastInterest.getAmount

NullPo

inters

!

Friday, July 19, 13

Page 5: Introduction to Option monad in Scala

Classic solutions (java)

Friday, July 19, 13

Page 6: Introduction to Option monad in Scala

Classic solutions (java)Nested if’s

if(customer != null {! if(customer.getAccount(FUNSTUFF) != null) {! ! if(customer.getAccount(FUNSTUFF).getLastInterest != null) {! ! ! return customer.getAccount(FUNSTUFF).getLastInterest.getAmount! ! }! }

}return null;

Friday, July 19, 13

Page 7: Introduction to Option monad in Scala

Classic solutions (java)Nested if’s

if(customer != null {! if(customer.getAccount(FUNSTUFF) != null) {! ! if(customer.getAccount(FUNSTUFF).getLastInterest != null) {! ! ! return customer.getAccount(FUNSTUFF).getLastInterest.getAmount! ! }! }

}return null; UGLY

Friday, July 19, 13

Page 8: Introduction to Option monad in Scala

Classic solutions (java)Nested if’s

if(customer != null {! if(customer.getAccount(FUNSTUFF) != null) {! ! if(customer.getAccount(FUNSTUFF).getLastInterest != null) {! ! ! return customer.getAccount(FUNSTUFF).getLastInterest.getAmount! ! }! }

}return null;

Early returnsif (customer == null) return null;if (customer.getAccount(FUNSTUFF) == null) return null;if (customer.getAccount(FUNSTUFF).getLastInterest == null) return null;return customer.getAccount(FUNSTUFF).getLastInterest.getAmount

UGLY

Friday, July 19, 13

Page 9: Introduction to Option monad in Scala

Classic solutions (java)Nested if’s

if(customer != null {! if(customer.getAccount(FUNSTUFF) != null) {! ! if(customer.getAccount(FUNSTUFF).getLastInterest != null) {! ! ! return customer.getAccount(FUNSTUFF).getLastInterest.getAmount! ! }! }

}return null;

Early returnsif (customer == null) return null;if (customer.getAccount(FUNSTUFF) == null) return null;if (customer.getAccount(FUNSTUFF).getLastInterest == null) return null;return customer.getAccount(FUNSTUFF).getLastInterest.getAmount

UGLY

Still UG

LY!

Friday, July 19, 13

Page 10: Introduction to Option monad in Scala

Same in Scala

Friday, July 19, 13

Page 11: Introduction to Option monad in Scala

Same in Scalaval customer = Customers.findById(1234)if (customer != null) {! val account = customer.account(FUNSTUFF);! if (account != null) {! ! val interest = account.getLastInterest! ! if (interest != null) ! ! ! interest.amount! ! else! ! ! null! } else! ! null} else! null

Friday, July 19, 13

Page 12: Introduction to Option monad in Scala

Same in Scalaval customer = Customers.findById(1234)if (customer != null) {! val account = customer.account(FUNSTUFF);! if (account != null) {! ! val interest = account.getLastInterest! ! if (interest != null) ! ! ! interest.amount! ! else! ! ! null! } else! ! null} else! null

Even in

Scala,

Still UG

LY!

Friday, July 19, 13

Page 13: Introduction to Option monad in Scala

Same in Scalaval customer = Customers.findById(1234)if (customer != null) {! val account = customer.account(FUNSTUFF);! if (account != null) {! ! val interest = account.getLastInterest! ! if (interest != null) ! ! ! interest.amount! ! else! ! ! null! } else! ! null} else! null

Even in

Scala,

Still UG

LY!

...and errorprone

Friday, July 19, 13

Page 14: Introduction to Option monad in Scala

non-existence

Friday, July 19, 13

Page 15: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Friday, July 19, 13

Page 16: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Groovy (et al.)Safe navigation operator

def amount = customer?.account?.interest?.amount

Friday, July 19, 13

Page 17: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Groovy (et al.)Safe navigation operator

def amount = customer?.account?.interest?.amount

Ceylon, Kotlin etc.both nullable and null-safe types...

String name = null; //compile error: null is not an instance of StringString? name = null; //OK

Friday, July 19, 13

Page 18: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Groovy (et al.)Safe navigation operator

def amount = customer?.account?.interest?.amount

Ceylon, Kotlin etc.both nullable and null-safe types...

String name = null; //compile error: null is not an instance of StringString? name = null; //OK

Others (e.g. Clojure): ‘nil’ type - close but...!

Friday, July 19, 13

Page 19: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Groovy (et al.)Safe navigation operator

def amount = customer?.account?.interest?.amount

Ceylon, Kotlin etc.both nullable and null-safe types...

String name = null; //compile error: null is not an instance of StringString? name = null; //OK

Others (e.g. Clojure): ‘nil’ type - close but...!

Scala ....

Friday, July 19, 13

Page 20: Introduction to Option monad in Scala

non-existenceJava

null, null, null, null :-(

Groovy (et al.)Safe navigation operator

def amount = customer?.account?.interest?.amount

Ceylon, Kotlin etc.both nullable and null-safe types...

String name = null; //compile error: null is not an instance of StringString? name = null; //OK

Others (e.g. Clojure): ‘nil’ type - close but...!

Scala .... patience...

Friday, July 19, 13

Page 21: Introduction to Option monad in Scala

We need something like:

Container Empty container

Important: Same ‘shape’ outside

Friday, July 19, 13

Page 22: Introduction to Option monad in Scala

Let me present:

Friday, July 19, 13

Page 23: Introduction to Option monad in Scala

Let me present:

Option monad

Friday, July 19, 13

Page 24: Introduction to Option monad in Scala

Let me present:

Option monadSHHH

Friday, July 19, 13

Page 25: Introduction to Option monad in Scala

Scala’s Option type:

Some(2) None

Friday, July 19, 13

Page 26: Introduction to Option monad in Scala

Option - concept

sealed trait Option[A]

case class Some[A](a: A) extends Option[A]case class None[A] extends Option[A]

Friday, July 19, 13

Page 27: Introduction to Option monad in Scala

Advantages

• Values that may or may not exist now stated in type system

• Clearly shows possible non-existence

• Compiler forces you to deal with it

• You won’t accidentally rely on value

Friday, July 19, 13

Page 28: Introduction to Option monad in Scala

Option - in RL

sealed abstract class Option[A] extends Product

case class Some[+A](a: A) extends Option[A]

case object None extends Option[Nothing]

Friday, July 19, 13

Page 29: Introduction to Option monad in Scala

Option - in RL sealed abstract class Option[A] extends Product { def isEmpty: Boolean def get: A ...}

final case class Some[+A](x: A) extends Option[A] { def isEmpty = false def get = x}

case object None extends Option[Nothing] { def isEmpty = true def get = throw new NoSuchElementException("None.get")}

Friday, July 19, 13

Page 30: Introduction to Option monad in Scala

WAT?

Friday, July 19, 13

Page 31: Introduction to Option monad in Scala

Creating Options

Friday, July 19, 13

Page 32: Introduction to Option monad in Scala

Creating Options• Direct: !

val o = Some(3) //> o : Option[Int] = Some(3)val n = None //> n : None.type = None

Friday, July 19, 13

Page 33: Introduction to Option monad in Scala

BUT NEVER: val aaargh = Some(null)

Creating Options• Direct: !

val o = Some(3) //> o : Option[Int] = Some(3)val n = None //> n : None.type = None

Friday, July 19, 13

Page 34: Introduction to Option monad in Scala

BUT NEVER: val aaargh = Some(null)

Creating Options• Direct: !

val o = Some(3) //> o : Option[Int] = Some(3)val n = None //> n : None.type = None

• Factory method on companion object: !val o = Option(3) //> o : Option[Int] = Some(3)val nn = Option(null) //> nn : Option[Null] = None

Friday, July 19, 13

Page 35: Introduction to Option monad in Scala

val schroedingersBox : Option[Cat] =! if(random.nextBoolean) then Some(Garfield) else None

Friday, July 19, 13

Page 36: Introduction to Option monad in Scala

Many mays to use• isDefined

• isEmpty

Friday, July 19, 13

Page 37: Introduction to Option monad in Scala

Many mays to use• isDefined

• isEmpty

if (customer.isDefined) ! customer.account;

Friday, July 19, 13

Page 38: Introduction to Option monad in Scala

Many mays to use• isDefined

• isEmpty

if (customer.isDefined) ! customer.account;

Much more type-safe and null-safe than original null-based java-flavour,

but code just as ugly

Friday, July 19, 13

Page 39: Introduction to Option monad in Scala

get?

three.get! //> res10: Int = 3

nope.get! //> java.util.NoSuchElementException: None.get

Friday, July 19, 13

Page 40: Introduction to Option monad in Scala

get?

three.get! //> res10: Int = 3

nope.get! //> java.util.NoSuchElementException: None.get

$> Yay. We can still write the other ugly version with Exception handling :-)

Friday, July 19, 13

Page 41: Introduction to Option monad in Scala

Apprentice level:Pattern matching

Friday, July 19, 13

Page 42: Introduction to Option monad in Scala

Apprentice level:Pattern matching

val foo = request.param("foo") match {

! case Some(foo) => foo ! case None => "Default foo"}

Friday, July 19, 13

Page 43: Introduction to Option monad in Scala

Apprentice level:Pattern matching

val foo = request.param("foo") match {

! case Some(foo) => foo ! case None => "Default foo"}

Sometimes useful, but...

Friday, July 19, 13

Page 44: Introduction to Option monad in Scala

Apprentice level:Pattern matching

val foo = request.param("foo") match {

! case Some(foo) => foo ! case None => "Default foo"}

Sometimes useful, but...

at some point a Jedi you must become Friday, July 19, 13

Page 45: Introduction to Option monad in Scala

What we really want is

Friday, July 19, 13

Page 46: Introduction to Option monad in Scala

What we really want is

... to do stuff with our values

Friday, July 19, 13

Page 47: Introduction to Option monad in Scala

What we really want is

... to do stuff with our values

Friday, July 19, 13

Page 48: Introduction to Option monad in Scala

But...

Friday, July 19, 13

Page 49: Introduction to Option monad in Scala

We want...?

Friday, July 19, 13

Page 50: Introduction to Option monad in Scala

Padawan level:functional

• Treat Option as a (very small) collection

• “Biased” towards Some

• map, flatMap etc.

• and compose to your desire when the option contains a value

Friday, July 19, 13

Page 51: Introduction to Option monad in Scala

map

Friday, July 19, 13

Page 52: Introduction to Option monad in Scala

map

! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3)

Friday, July 19, 13

Page 53: Introduction to Option monad in Scala

map

! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3)! val res = three.map(_ + 3)

Friday, July 19, 13

Page 54: Introduction to Option monad in Scala

map

! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3)! val res = three.map(_ + 3)

> res: Option[Int] = Some(6)

Friday, July 19, 13

Page 55: Introduction to Option monad in Scala

map

option.map(foo(_))

equivalent to:

option match { case None => None case Some(x) => Some(foo(x))}

Friday, July 19, 13

Page 56: Introduction to Option monad in Scala

Examples

Friday, July 19, 13

Page 57: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}

Examples

Friday, July 19, 13

Page 58: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

Examples

Friday, July 19, 13

Page 59: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i))

Examples

Friday, July 19, 13

Page 60: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i)) //> res4: Option[Int] = Some(9)

Examples

Friday, July 19, 13

Page 61: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i)) //> res4: Option[Int] = Some(9)three.map(sqr(_))

Examples

Friday, July 19, 13

Page 62: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i)) //> res4: Option[Int] = Some(9)three.map(sqr(_)) //> res5: Option[Int] = Some(9)

Examples

Friday, July 19, 13

Page 63: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i)) //> res4: Option[Int] = Some(9)three.map(sqr(_)) //> res5: Option[Int] = Some(9)three.map(sqr)

Examples

Friday, July 19, 13

Page 64: Introduction to Option monad in Scala

def sqr(i:Int) = {i*i}val three = Option(3)

three.map(i => sqr(i)) //> res4: Option[Int] = Some(9)three.map(sqr(_)) //> res5: Option[Int] = Some(9)three.map(sqr) //> res6: Option[Int] = Some(9)

Examples

Friday, July 19, 13

Page 65: Introduction to Option monad in Scala

flatMap

option.flatMap(foo(_))

is equivalent to:

option match { case None => None case Some(x) => foo(x)}

Friday, July 19, 13

Page 66: Introduction to Option monad in Scala

three.flatMap(x => Some(x.toString)) Option[java.lang.String] = Some(3)

nah.flatMap(x => Some(x.toString)) Option[java.lang.String] = None

Friday, July 19, 13

Page 67: Introduction to Option monad in Scala

Side effects:foreach

option.foreach(foo(_))

is equivalent to:

option match { case None => {} case Some(x) => foo(x)}

Friday, July 19, 13

Page 68: Introduction to Option monad in Scala

three.foreach(println(_))

Friday, July 19, 13

Page 69: Introduction to Option monad in Scala

val userOpt = UserDao.findById(userId)

userOpt.foreach(user => println(user.name))

or, even shorter:

userOpt.foreach(println)

Friday, July 19, 13

Page 70: Introduction to Option monad in Scala

Working with lists

val o1 = Option(1)! ! //> o1 : Option[Int] = Some(1)val o2 = Option(2) //> o2 : Option[Int] = Some(2)val o3 = Option(3) //> o3 : Option[Int] = Some(3)val l = List(o1, nope, o2, nah, o3)

! //> l : List[Option[Int]] = List(Some(1), None, Some(2), None, Some(3))

!l.map(_.map(sqr)) ! ! //> res8: List[Option[Int]]

= List(Some(1), None, Some(4), None, Some(9))

l.flatMap(_.map(sqr))! ! //> res9: List[Int] = List(1, 4, 9)

Friday, July 19, 13

Page 71: Introduction to Option monad in Scala

Jedi level:for comprehesions

val ageOpt = for {! user <- UserDao.findById(userId)! age <- user.ageOpt} yield age

Friday, July 19, 13

Page 72: Introduction to Option monad in Scala

Jedi mind tricks//we have a ‘User’ with mandatory name, but optional age

case class User(val name:String , val age:Option[Int]) def prettyPrint(user: User) =! List(Option(user.name), user.age).flatten.mkString(", ")

val foo = User("Foo", Some(42))val bar = User("Bar", None)

prettyPrint(foo) //prints "Foo, 42"prettyPrint(bar) //prints "Bar"

Friday, July 19, 13

Page 73: Introduction to Option monad in Scala

val userOpt = UserDao.findById(userId) OrElse Some(UserDao.create)

or:

val user = UserDao.findById(userId) getOrElse UserDao.create

Friday, July 19, 13

Page 74: Introduction to Option monad in Scala

other option optionsdef filter(p: A => Boolean): Option[A] def exists(p: A => Boolean): Boolean

foldcollectiteratortoList

Friday, July 19, 13

Page 75: Introduction to Option monad in Scala

ResourcesReferences, Thanks, Resources and further

reading

Attributions:

Thanks to Adit Bhargava for a great blogpost on monads in Haskel and for letting me use his cartoon drawings: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

For broadening my mind on higher-order use of Options: http://blog.tmorris.net/posts/scalaoption-cheat-sheet/

Further reading

http://marakana.com/static/courseware/scala/presentation/comprehending-monads.html

http://blog.xebia.com/2011/06/02/scala-options-the-slick-way/

Friday, July 19, 13