KGI

blog blog blog

Twitter GitHub LinkedIn Lanyrd Email Feed

Box Cheat Sheet for Lift

When you’re learning Scala, one of the first concepts that clicks is pattern matching. Once you get the “hammer” of pattern matching in your Scala toolbox, everything is a nail; you want to use it everywhere for everything. Pattern matching is very powerful and easy to use, but it turns out for many, many simple cases using a higher-order function is more concise and performant. I have Tony Morris’s Option Cheat Sheet http://blog.tmorris.net/scalaoption-cheat-sheet/ bookmarked and use it almost every day.

In Lift, though, we generally use Box instead of Option. Box adds a third Failure state and a host of additional methods. I got tired of translating the Option methods to Box ones in my head so I wrote up this translation of Box methods to match statements. I used the Lift API Docs and the Lift source code to compile it.

The next hammer-that-turns-everything-into-a-nail that I encountered in learning Scala was for-comprehensions. I’m still kind of in love with them! In the interest of length, I haven’t included any examples here, but Box plays very nicely with for-comprehensions as well. I hope to cover some ways we use Box in for-comprehensions in a future post.

flatMap
theBox.flatMap(foo(_))
foo returns a Box of any type

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => foo(x)
}

map
theBox.map(foo(_))
foo returns any type

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => Full(foo(x))
}

dmap
Equivalent to .map(foo(_)).openOr(bar)
theBox.dmap(bar)(foo(_))
foo returns a value of the same type as bar

1
2
3
4
5
theBox match {
  case Empty => bar
  case Failure(message, exception, chain) => bar
  case Full(x) => foo(x)
}

choice
Equivalent to .flatMap(foo(_)).or(bar())
theBox.choice(foo(_))(bar())
foo and bar are functions that return Boxes of the same type

1
2
3
4
5
theBox match {
  case Empty => bar()
  case Failure(message, exception, chain) => bar()
  case Full(x) => foo(x)
}

===
theBox === bar
bar is a value of any type

1
2
3
4
5
theBox match {
  case Empty => false
  case Failure(message, exception, chain) => false
  case Full(x) => x.equals(bar)
}

equals
Determines equality based upon the the Box’s content. In the case of two Failures being compared, the causes must match exactly.
theBox.equals(bar)
bar is a value of any type

1
2
3
4
5
6
7
(theBox, bar) match {
  case (Empty, Empty) => true
  case (Failure(msg1, ex1, chain1), Failure(msg2, ex2, chain2)) => (msg1, ex1, chain1) == (msg2, ex2, chain2)
  case (Full(x), Full(y)) => x.equals(y)
  case (Full(x), y) => x.equals(y)
  case _ => false
}

isEmpty
This one can be tricky!
theBox.isEmpty

1
2
3
4
5
theBox match {
  case Empty => true
  case Failure(message, exception, chain) => true
  case Full(x) => false
}

isDefined
theBox.isDefined

1
2
3
4
5
theBox match {
  case Empty => false
  case Failure(message, exception, chain) => false
  case Full(x) => true
}

exists
theBox.exists(foo(_))
foo returns Boolean

1
2
3
4
5
theBox match {
  case Empty => false
  case Failure(message, exception, chain) => false
  case Full(x) => foo(x)
}

forall
theBox.forall(foo(_))
foo returns Boolean

1
2
3
4
5
theBox match {
  case Empty => true
  case Failure(message, exception, chain) => true
  case Full(x) => foo(x)
}

foreach
theBox.foreach(foo(_))
foo returns Unit

1
2
3
4
5
theBox match {
  case Empty => {}
  case Failure(message, exception, chain) => {}
  case Full(x) => foo(x)
}

pass
theBox.pass(foo(_))
foo takes a Box and returns Unit

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => foo(theBox); theBox
}

filter
theBox.filter(foo(_))
foo returns Boolean

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => if (foo(x)) Full(x) else Empty
}

filterMsg
Returns a Failure with the provided message if the predicate is not met.
theBox.filterMsg(msg)(foo(_))
foo returns Boolean

1
2
3
4
5
theBox match {
  case Empty => Failure(msg, Empty, Empty)
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => if (foo(x)) Full(x) else Failure(msg, Empty, Empty)
}

filterNot
theBox.filterNot(foo(_))
foo returns Boolean

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => if (!foo(x)) Full(x) else Empty
}

openOr
box openOr(bar)
bar is a value of the same type or a descendant of the Box’s type

1
2
3
4
5
theBox match {
  case Empty => bar
  case Failure(message, exception, chain) => bar
  case Full(x) => x
}

or
theBox.or(bar)
bar is a Box of the same type or a descendant of the Box’s type

1
2
3
4
5
theBox match {
  case Empty => bar
  case Failure(message, exception, chain) => bar
  case Full(x) => Full(x)
}

toOption
Box defines an implicit conversion from Box[T] to Option[T], so you can call Option methods on Boxes if you want. This is how that implicit conversion is defined (you also can call .toOption directly.)
theBox.toOption

1
2
3
4
5
theBox match {
  case Empty => None
  case Failure(message, exception, chain) => None
  case Full(x) => Some(x)
}

toList
Box also defines an implicit conversion to Iterable, so you can call any methods from Iterable that you find useful. Note, though, that if the Box is full .toList will always return a List with one element. If you want to call Iterable methods on the value of a Box[List[Foo]] you probably want to use .elements.
theBox.toList

1
2
3
4
5
theBox match {
  case Empty => Nil
  case Failure(message, exception, chain) => Nil
  case Full(x) => List(x)
}

elements
.elements returns an Iterator over the value of the box. Good for manipulating the contents of a Box[List[Foo]].
theBox.elements

1
2
3
4
5
theBox match {
  case Empty => Iterator.empty
  case Failure(message, exception, chain) => Iterator.empty
  case Full(x) => Iterator(x)
}

isA
theBox.isA[Bar]
Bar is a Class or primitive

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Empty
  case Full(x) => if (Bar.isAssignableFrom(x.getClass)) Full(value.asInstanceOf[Bar]) else Empty
}

asA
theBox.asA[Bar]
Bar is a Class or primitive

1
2
3
4
5
theBox match {
  case Empty => Empty
  case Failure(message, exception, chain) => Empty
  case Full(x) => if (Full(x).isA[Bar]) then Full(x).asInstanceOf[Box[Bar]] else Empty
}

?~
Useful in for-comprehensions.
box ?~ emptyMsg
emptyMsg is a string

1
2
3
4
5
theBox match {
  case Empty => Failure(emptyMsg, Empty, Empty)
  case Failure(message, exception, chain) => Failure(message, exception, chain)
  case Full(x) => Full(x)
}

?~!
Like ?~, but if the Box is already a Failure it replaces the message and chains the existing Failure.
box ?~! failMsg
emptyMsg is a string

1
2
3
4
5
theBox match {
  case Empty => Failure(failMsg, Empty, Empty)
  case Failure(message, exception, chain) => Failure(failMsg, Empty, Full(box))
  case Full(x) => Full(x)
}


Constructors

from Option
Box(foo: Option[T])

1
2
3
4
foo match {
  case Some(x) => Full(x)
  case None => Empty
}

from List
This returns a Box with the head of the list, if the list isn’t empty.
Box(foo: List[T])

1
2
3
4
foo match {
  case x :: _ => Full(x)
  case Nil => Empty
}

null-safe
This converts null values to Empty, which is very useful when you’re dealing with values from Java code.
Box !! bar

1
2
3
4
bar match {
    case null => Empty
    case _ => Full(bar)
}

EmptyBox
While Box’s advantage over Option is that it distinguishes between the empty case and the failure case, if you don’t care whether a Box is a Failure or an Empty, you can match on EmptyBox.

The following code:

1
2
3
4
5
theBox match {
  case Empty => foo
  case Failure(message, exception, chain) => foo
  case Full(x) => bar
}

is equivalent to:

1
2
3
4
theBox match {
  case EmptyBox => foo
  case Full(x) => bar
}

tryo
An insanely useful helper method that catches exceptions and converts them to Failures. You can also pass a list of Exception classes that should be converted to Emptys instead, and/or a callback function that should be triggered if an exception is thrown.
tryo(foo(bar))
foo is a function that can throw an exception

1
2
3
4
5
try {
  Full(foo(bar))
} catch {
  case ex: Failure(ex.getMessage, Full(ex), Empty)
}