Suma
Suma

Reputation: 34403

Nicer alternatives to Option.get when get is guaranteed to succeed

One of the reasons why Scalaz Maybe was added was to get rid of get:

Some differences from std lib Option:

  • No unsafe get method

I understand get can be unsafe, but sometimes is is safe (in a code which has verified before the Option is not empty). There are some situations where one can easily stay away from using get, but sometimes it is not so obvious to me. What would be the alternatives to get in the following code?

def point(cursor:Int) : Option[XY]

def paste(cs: Seq[Int]) = {
  if (validateSelection(cs)) {
    cs.size match {
      case 2 => // between two points
        // editablePoint returning Option for general case,
        // but here it is known to succeed as validateSelection passed
        val beDiff = point(cs.head).get.xy - point(cs.last).get.xy
        ...
    case _ =>
      ...
  }
  ...
}

Upvotes: 2

Views: 565

Answers (1)

phadej
phadej

Reputation: 12123

To get rid of the quasi-safe get, you have to refactor your validateSection. In your example it probably returns a Boolean:

def validateSection(cs: Seq[Int]): Boolean

Yet, you could return an evidence, that cs is valid:

/* Returns the first and last values of the sequence,
 * if the sequence's length == 2. Otherwise `Empty`.
 */
def validateSection(cs: Seq[Int]): Maybe[(Int, Int)]

Then you could refactor your snippet as:

def paste(cs: Seq[Int]) = {
  match validateSelection(cs) {
    case (first, last) => 
      val beDiff = first - last
        ...
    case _ =>
      ...
  }
  ...
}

EDIT You can take the idea further: Create auxillary types to get away of function's partiality.

The point returns Option[XY], i.e. it's partial. We should try to make input parameters more precise, so point can be total, i.e. return XY. Without knowing precise specification I can only sketch the solution:

case class Selection(/* ... */)

/* Constructed from `Selection` */
case class Cursor()

object Selection {
  def first: Cursor = ???
  def last: Cursor = ???
}

/* If `cs` is valid, we return a `ValidSelection`, othersise `Empty`. */
def validateSelection(cs: Seq[Int]): Maybe[Selection]

/* With more precise type, we can have total `point`. */
def point(cursor: Cursor): XY

This approach may look boilerplaty at first, but here we use the advantage of having the type system. Avoid stringly typed or List-typed programming.

Upvotes: 1

Related Questions