Scala: elegant way to use require to validate multiple options?

I have two solutions for this problem. I do not like both of them so I was wondering if there's a more elegant solution.

import java.util.Date
import scala.math.Ordered.orderingToOrdered

// Solution # 1:
case class A(startDate: Option[Date] = None,
             endDate: Option[Date]   = None) {
  require(if (startDate.isEmpty && endDate.isEmpty) false else true, 
          "Either startDate or endDate must be defined")
  require(if (startDate.isDefined && endDate.isDefined) startDate.get < endDate.get else true,  
          s"startDate:${startDate.get} must be less than endDate:${endDate.get}")
  // Problem: multiple checks using isEmpty and isDefined followed by .get
}

// Solution # 2:
case class B(startDate: Option[Date] = None,
             endDate: Option[Date]   = None) {
  val (requirement, msg) = (startDate, endDate) match {
    case (None, None)                  => false -> "Either startDate or endDate must be defined"
    case (Some(s), Some(e)) if (s > e) => false -> s"startDate:$s must be less than endDate:$e"
    case _                             => true  -> "OK" // Problem: redundant statement
  }
  require(requirement, msg)
}

Conditions:

  1. Both startDate or endDate can be None
  2. startDate cannot be greater than endDate

Answers


Here is how I would write it:

import java.util.Date

case class MyTest(startDate: Option[Date] = None, endDate: Option[Date] = None) {
  require(startDate.isDefined || endDate.isDefined,
    "Either startDate or endDate must be defined")
  require(!(startDate.isDefined && endDate.isDefined) || (startDate.get.before(endDate.get)),
    s"startDate: ${startDate.get} must be less than endDate:${endDate.get}")
}

object Test extends App {
  // Gives "Either startDate or endDate must be defined" as expected
  //val m1 = MyTest(None, None)

  // These run OK
  val m2 = MyTest(Some(new Date(1234)), None)
  val m3 = MyTest(None, Some(new Date(4321)))
  val m4 = MyTest(Some(new Date(1234)), Some(new Date(4321)))

  // Gives "startDate: Thu Jan 01 01:00:00 CET 1970 must be less than endDate: Thu Jan 01 01:00:00 CET 1970" as expected
  //val m4 = MyTest(Some(new Date(4321)), Some(new Date(1234)))
}

No need for these if inside the requires. Also note the second require uses "not(a) or b" is equivalent to "a => b". Last but not least, as you check for your options to be defined, then you are safe when doing the .get on your options.


Need Your Help

How do I tell valgrind to memcheck forked processes?

c++ c linux fork valgrind

I have a process x that I want to check for leaks with valgrind. The problem is that x is run by y, and y in turn is run by z. I can't run x standalone because y and z setup the environment for x...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.