• Like
  • Save
Introduction to Option monad in Scala
Upcoming SlideShare
Loading in...5
×
 

Introduction to Option monad in Scala

on

  • 1,031 views

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. ...

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.

Statistics

Views

Total Views
1,031
Views on SlideShare
1,019
Embed Views
12

Actions

Likes
4
Downloads
12
Comments
0

2 Embeds 12

http://www.linkedin.com 7
https://twitter.com 5

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Introduction to Option monad in Scala Introduction to Option monad in Scala Presentation Transcript

    • Some(slides) val reasonsToUseNull = None Friday, July 19, 13
    • 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
    • Oh we wish... val customer = Customers.findById(1234) customer.getAccount(FUNSTUFF).getLastInterest.getAmount Friday, July 19, 13
    • Oh we wish... val customer = Customers.findById(1234) customer.getAccount(FUNSTUFF).getLastInterest.getAmount NullPointers ! Friday, July 19, 13
    • Classic solutions (java) Friday, July 19, 13
    • 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
    • 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
    • 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 returns if (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
    • 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 returns if (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 UGLY! Friday, July 19, 13
    • Same in Scala Friday, July 19, 13
    • Same in Scala val 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
    • Same in Scala val 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 UGLY! Friday, July 19, 13
    • Same in Scala val 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 UGLY! ...and errorprone Friday, July 19, 13
    • non-existence Friday, July 19, 13
    • non-existence Java null, null, null, null :-( Friday, July 19, 13
    • non-existence Java null, null, null, null :-( Groovy (et al.) Safe navigation operator def amount = customer?.account?.interest?.amount Friday, July 19, 13
    • non-existence Java 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 String String? name = null; //OK Friday, July 19, 13
    • non-existence Java 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 String String? name = null; //OK Others (e.g. Clojure): ‘nil’ type - close but...! Friday, July 19, 13
    • non-existence Java 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 String String? name = null; //OK Others (e.g. Clojure): ‘nil’ type - close but...! Scala .... Friday, July 19, 13
    • non-existence Java 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 String String? name = null; //OK Others (e.g. Clojure): ‘nil’ type - close but...! Scala .... patience... Friday, July 19, 13
    • We need something like: Container Empty container Important: Same ‘shape’ outside Friday, July 19, 13
    • Let me present: Friday, July 19, 13
    • Let me present: Option monad Friday, July 19, 13
    • Let me present: Option monadSHHH Friday, July 19, 13
    • Scala’s Option type: Some(2) None Friday, July 19, 13
    • 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
    • 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
    • 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
    • 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
    • WAT? Friday, July 19, 13
    • Creating Options Friday, July 19, 13
    • Creating Options • Direct: ! val o = Some(3) //> o : Option[Int] = Some(3) val n = None //> n : None.type = None Friday, July 19, 13
    • 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
    • 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
    • val schroedingersBox : Option[Cat] = ! if(random.nextBoolean) then Some(Garfield) else None Friday, July 19, 13
    • Many mays to use • isDefined • isEmpty Friday, July 19, 13
    • Many mays to use • isDefined • isEmpty if (customer.isDefined) ! customer.account; Friday, July 19, 13
    • 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
    • get? three.get ! //> res10: Int = 3 nope.get ! //> java.util.NoSuchElementException: None.get Friday, July 19, 13
    • 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
    • Apprentice level: Pattern matching Friday, July 19, 13
    • Apprentice level: Pattern matching val foo = request.param("foo") match { ! case Some(foo) => foo ! case None => "Default foo" } Friday, July 19, 13
    • 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
    • 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
    • What we really want is Friday, July 19, 13
    • What we really want is ... to do stuff with our values Friday, July 19, 13
    • What we really want is ... to do stuff with our values Friday, July 19, 13
    • But... Friday, July 19, 13
    • We want...? Friday, July 19, 13
    • 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
    • map Friday, July 19, 13
    • map ! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3) Friday, July 19, 13
    • map ! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3) ! val res = three.map(_ + 3) Friday, July 19, 13
    • map ! val three = Some(3) ! ! ! ! > three : Option[Int] = Some(3) ! val res = three.map(_ + 3) > res: Option[Int] = Some(6) Friday, July 19, 13
    • map option.map(foo(_)) equivalent to: option match { case None => None case Some(x) => Some(foo(x)) } Friday, July 19, 13
    • Examples Friday, July 19, 13
    • def sqr(i:Int) = {i*i} Examples Friday, July 19, 13
    • def sqr(i:Int) = {i*i} val three = Option(3) Examples Friday, July 19, 13
    • def sqr(i:Int) = {i*i} val three = Option(3) three.map(i => sqr(i)) Examples Friday, July 19, 13
    • 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
    • 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
    • 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
    • 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
    • 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
    • flatMap option.flatMap(foo(_)) is equivalent to: option match { case None => None case Some(x) => foo(x) } Friday, July 19, 13
    • 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
    • Side effects: foreach option.foreach(foo(_)) is equivalent to: option match { case None => {} case Some(x) => foo(x) } Friday, July 19, 13
    • three.foreach(println(_)) Friday, July 19, 13
    • val userOpt = UserDao.findById(userId) userOpt.foreach(user => println(user.name)) or, even shorter: userOpt.foreach(println) Friday, July 19, 13
    • 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
    • Jedi level: for comprehesions val ageOpt = for { ! user <- UserDao.findById(userId) ! age <- user.ageOpt } yield age Friday, July 19, 13
    • 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
    • val userOpt = UserDao.findById(userId) OrElse Some(UserDao.create) or: val user = UserDao.findById(userId) getOrElse UserDao.create Friday, July 19, 13
    • other option options def filter(p: A => Boolean): Option[A] def exists(p: A => Boolean): Boolean fold collect iterator toList Friday, July 19, 13
    • Resources References, 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