# Null is not an Option # Either are NPEs
## Me * Charles O'Farrell * Atlassian * [@charlesofarrell](http://twitter.com/charlesofarrell)
## What's wrong with this code? ```java public Foo someMethod() someMethod().doSomething() ```
## Look again ```java @Nullable public Foo someMethod() someMethod().doSomething() ```
## By the power of Greyskull ```java public Nullable<Foo> someMethod() // Won't compile someMethod().doSomething() ```
## Introducing Option ```scala sealed abstract class Option[A] case class Some[+A](x: A) extends Option[A] case object None extends Option[Nothing] ``` - Collection with none or one element
## Revenge of the null ```java Map<String, Conflict> conflicts = ... InternalChange change = parseChange(line); Conflict conflict = conflicts.get(change.getPath()); if (conflict == null) { conflict = conflicts.get(change.getSrcPath()); } if (conflict != null) { change = builder.conflict(conflict).build(); } ```
## java.util.Map ```java class Map<K, V> { @Nullable V get(K key) } ```
## Scala Map ```scala class Map[K, V] { Option[V] get(K key) } ```
## More Options ```scala trait Option[A] { def isEmpty: Boolean def isDefined: Boolean def get: A // throws NoSuchElementException } ```
## Option as null ```scala val conflicts: Map[String, Conflict] = ... var change: InternalChange = parseChange(line) var conflict: Option[Conflict] = conflicts.get(change.getPath) if (conflict.isEmpty) { conflict = conflicts.get(change.getSrcPath) } if (conflict.isDefined) { change = builder.conflict(conflict.get).build } ```
## Option as null * Already better than null * Type-safety vs documentation * Developers have to stop and think * But, get is ugly...
## Use Option or else ```scala abstract class Option[A] { def orElse[B >: A](alternative: => Option[B]): Option[B] } ``` Note "`: =>`" is evaluated lazily
## Composing Option ```scala val conflictOption: Option[Conflict] = conflicts.get(change.getPath) .orElse(conflicts.get(change.getSrcPath)) for (conflict <- conflictOption) { change = builder.conflict(conflict).build } ```
## Seems familiar ```js var value = map['a'] || map['b'] || 'c' ``` ```Scala val value: Option[String] = map.get("a").orElse(map.get("b")).orElse(Some("c")) ```
## Get or else ```scala abstract class Option[A] { def getOrElse[B >: A](default: => B): B } ```
## Seems familiar (part 2) ```js var value = map['a'] || map['b'] || 'c' ``` ```scala val value: String = map.get("a").orElse(map.get("b")).getOrElse("c") ```
## Seen this before? ```java InetSocketAddress socketAddress = session.getIoSession().getRemoteAddress(); if (socketAddress != null) { InetAddress inetAddress = socketAddress.getAddress(); if (inetAddress != null) { remoteAddress = inetAddress.getHostAddress(); } } ```
## Not a null in sight ```scala val remoteAddress: Option[String] = for { socketAddress <- session.getIoSession.getRemoteAddress inetAddress <- socketAddress.getAddress } yield inetAddress.getHostAddress ```
Option all the things
## Is Option a better null? - Type safety - Just a collection - Can filter/map/concat/etc - Composition!
## Another example ```java public ModelAndView browseFilePath() { Repository repository = getRepository(projectKey, repoSlug); if (repository == null) { return handleEmptyRepo(); } Branch defaultBranch = getDefaultBranch(repository); if (defaultBranch == null) { return handleNoDefaultBranch(repository); } return doSomething(defaultBranch); } ```
## What do we want? ```scal def browseFilePath: ModelAndView = { doSomething(getRepositoryAndBranch) } def diffFilePath: ModelAndView = { doSomethingElse(getRepositoryAndBranch) } ```
## Could we use Option? ```scala def getRepository: Option[Repository] def getDefaultBranch: Option[Branch] ```
## Could we use Option? ```scala def browseFilePath: Option[ModelAndView] = { for { repository <- getRepository(projectKey, repoSlug) defaultBranch <- getDefaultBranch(repository) } yield doSomething(defaultBranch) // TODO What about handleEmptyRepo or handleNoDefaultBranch? } ```
## Could we use Option? * In short - no!
## Use Exceptions? ```java public Repository getRepository() throws ModelAndViewException { Repository repository = repositoryService.findBySlug(projectKey, repoSlug); if (repository == null) { throw new ModelAndViewException(handleEmptyRepo()); } return repository; } ``` ```java public Branch getRepositoryAndBranch() throws ModelAndViewException { Repository repository = getRepository(projectKey, repoSlug); return getDefaultBranch(repository); } ```
## I take Exception to that ```java public ModelAndView browseFilePath() { try { return doSomething(getRepositoryAndBranch()); } catch (ModelAndViewException e) { return e.getModelAndView(); } } ``` ```java public ModelAndView diffFilePath() { try { return doSomethingElse(getRepositoryAndBranch()); } catch (ModelAndViewException e) { return e.getModelAndView(); } } ```
## What is this really saying? ```java Repository getRepository() throws ModelAndViewException ``` Can this be expressed as a function - only one return type?
Return _either_ ModelAndView or Repository ```scala def getRepository: Either[ModelAndView, Repository] ```
## Either ```scala sealed abstract class Either[+L, +R] case class Left[+L, +R](a: L) extends Either[L, R] case class Right[+L, +R](b: R) extends Either[L, R] ```
## Either as exceptions ```scala def getRepository: Either[ModelAndView, Repository] = { val repository: Option[Repository] = repositoryService.findBySlug(projectKey, repoSlug) repository.map(value => Right(value)).getOrElse(Left(handleEmptyRepo)) } ```
## To the Right ```scala class Option[+A] { def toRight[L](left: => L): Either[L, A] } ```
## To the Right ```scala def getRepository: Either[ModelAndView, Repository] = { repositoryService.findBySlug(projectKey, repoSlug).toRight(Left(handleEmptyRepo)) } ```
## Either Projection ```scala sealed abstract class Either[+L, +R] { def left: Projection[L] def right: Projection[R] } class Projection[A] { def get: A // throws NoSuchElementException } ```
## Either to the rescue ```scala def getRepositoryAndBranch: Either[ModelAndView, Branch] = for { repository <- getRepository(projectKey, repoSlug).right branch <- getDefaultBranch(repository).right } yield branch ```
## Are we there yet? ```scala def browseFilePath = { for (branch <- getRepositoryAndBranch.right) yield doSomething(branch) } def diffFilePath = { for (branch <- getRepositoryAndBranch.right) yield doSomethingElse(branch) } ```
## Not quite ```scala def browseFilePath: Either[ModelAndView, ModelAndView] = ... ``` ```scala object Either { implicit def e2m[A](x: Either[A, A]): MergeableEither[A] = new MergeableEither(x) class MergeableEither[A](x: Either[A, A]) { def merge: A } } ```
## Either - Like checked exceptions - But just a return value
## Question - How does for comprehension work?
## From before ```scala def getRepositoryAndBranch: Either[ModelAndView, Branch] = { repository <- getRepository(projectKey, repoSlug).right branch <- getDefaultBranch(repository).right } yield branch ```
## Syntactic Sugar ```scala def getRepositoryAndBranch: Either[ModelAndView, Branch] = { getRepository(projectKey, repoSlug).right .flatMap(repository => getDefaultBranch(repository)).right .map(branch => branch) } ```
## Down the rabbit hole ```scala class RightProjection[L, R] { def map[Y](f: R => Y): Either[L, R] def flatMap[LL >: L, Y](f: R => Either[LL, Y]): Either[LL, Y] } ```
## Syntactic Sugar ```scala def browseFilePath = { getRepositoryAndBranch.right.map(doSomething).merge } ```
## From before ```scala val remoteAddress: Option[String] = for { socketAddress <- session.getIoSession.getRemoteAddress inetAddress <- socketAddress.getAddress } yield inetAddress.getHostAddress ```
## Even more Options ```scala abstract class Option[A] { def map[B](f: A => B): Option[B] def flatMap[B](f: A => Option[B]): Option[B] } ```
## Syntactic Sugar ```scala session.getIoSession.getRemoteAddress .flatMap(socketAddress => socketAddress.getAddress) .map(inetAddress => inetAddress.getHostAddress) ```
## Abstract all the things ```scala abstract class Option[A] { def map[B](f: A => B): Option[B] def flatMap[B](f: A => Option[B]): Option[B] } ``` ```scala class RightProjection[L, R] { def map[Y](f: R => Y): Either[L, R] def flatMap[LL >: L, Y](f: R => Either[LL, Y]): Either[LL, Y] } ```
## What shall we call it? ```scala trait WarmFuzzyThing[A] { def map[B](f: A => B): WarmFuzzyThing[B] def flatMap[B](f: A => WarmFuzzyThing[B]): WarmFuzzyThing[B] } ```
## Monad! ```scala trait Monad[A] { def map[B](f: A => B): Monad[B] def flatMap[B](f: A => Monad[B]): Monad[B] } ``` Actually not quite...
## Mo Monad ```scala trait Monad[M[_]] { def map[A, B](fa: M[A], f: A => B): M[B] def flatMap[A, B](fa: M[A], f: A => M[B]): M[B] } ```
## Monads are out there ![](http://images-cdn01.associatedcontent.com/image/A2034/203477/300_203477.jpg)
## Question - Can we get rid of those pesky 'rights'?
## Biased [Tony Morris](https://groups.google.com/d/msg/scala-debate/K6wv6KphJK0/l8i0SSw6NlIJ) > Some history. I wrote scala.Either. > Originally, it was Right-biased. A peer convinced me to remove the bias [...]. > I have since regretted it.
## Shit just got real ```scala import scalaz._ import Scalaz._ ```
## Right-biased Either ```scala sealed trait \/[+L, +R] object \/ { def left[L, R](l: L) = -\/(l) def right[L, R](r: R) = \/-(r) } ```
## Ascii Madness ```scala def getRepository: \/[Model, Repository] ``` Same as: ```scala def getRepository: Model \/ Repository ```
## \o/ ```scala def getRepositoryAndBranch: ModelAndView \/ Branch = for { repository <- getRepository(projectKey, repoSlug) branch <- getDefaultBranch(repository) } yield branch ``` ```scala def browseFilePath = { getRepositoryAndBranch.map(doSomething).merge } ```
## Other uses? - What about validating forms?
## Validation ```scala def validateUsername(username: String): Exception \/ String def validateEmail(email: String): Exception \/ String ``` ```scala val user: Exception \/ User = for { username <- validateUsername(request.get("username")) email <- validateEmail(request.get("email")) } yield createUser(username, email) ```
## What about multiple errors? - Hmm, we want something a little different... - List[Exception] \/ User
## Validation ```scala sealed trait Validation[+E, +A] case class Success[E, A](a: A) extends Validation[E, A] case class Failure[E, A](e: E) extends Validation[E, A] type ValidationNEL[+E, +X] = Validation[NonEmptyList[E], X] ```
## Validation in action ```scala def validateEmail(email: String): ValidationNEL[Exception, String] = { if (email.contains("@")) { email.success } else { (new RuntimeException("Bad email")).failureNel } } ```
## Question - How will we compose them?
## Applicative Builder ```scala def createUser(username: String, email: String): User ``` ```scala val validations: ValidationNEL[Exception, User] = (validateUsername(username) |@| validateEmail(email))(createUser) ```
## Back to Option ```scala def createUser(username: String, email: String): User ``` ```scala val user: Option[User] = (map.get("username") |@| map.get("email"))(createUser) ```
## The Macaulay Culkin Operator ![](http://typeclassopedia.bitbucket.org/images/macaulay.jpg)
## Further Reading - [Scala Typeclassopedia](http://marakana.com/s/scala_typeclassopedia_with_john_kodumal_of_atlassian_video,1198/index.html) - [Learning Scalaz](http://eed3si9n.com/learning-scalaz-day7)