1

У меня есть два решения этой проблемы. Я не люблю их обоих, поэтому мне было интересно, есть ли более элегантное решение.Scala: элегантный способ использования требует проверки нескольких параметров?

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) 
} 

Условия:

  1. Оба STARTDATE или ENDDATE не может быть ни один
  2. STARTDATE не может быть больше, чем ENDDATE
+0

Является ли scalaz вариантом? Вы можете перейти от опции к валидации –

+0

@JustinPihony Yeah. Можете ли вы привести пример? –

ответ

2

Вот как я бы писать:

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))) 
} 

Нет необходимости в этих if внутри require с. Также обратите внимание, что второй require использует "not(a) or b" is equivalent to "a => b". И последнее, но не менее важное: по мере того, как вы проверяете, что ваши параметры будут определены, тогда вы будете в безопасности при выполнении .get по вашим параметрам.

+0

Это особенно сложно на глаза. –

1

Просто немного крейгслист объявление для ensuring:

scala> val a = Option(2); val b = Option(3) 
a: Option[Int] = Some(2) 
b: Option[Int] = Some(3) 

scala> (for (x <- a; y <- b) yield { require(x < y); y - x }).ensuring(_.nonEmpty) 
res0: Option[Int] = Some(1) 

scala> val a = Option(42) 
a: Option[Int] = Some(42) 

scala> (for (x <- a; y <- b) yield { require(x < y); y - x }).ensuring(_.nonEmpty) 
java.lang.IllegalArgumentException: requirement failed 
    at scala.Predef$.require(Predef.scala:207) 
    at $anonfun$1$$anonfun$apply$1.apply$mcII$sp(<console>:10) 
    at $anonfun$1$$anonfun$apply$1.apply(<console>:10) 
    at $anonfun$1$$anonfun$apply$1.apply(<console>:10) 
    at scala.Option.map(Option.scala:146) 
    at $anonfun$1.apply(<console>:10) 
    at $anonfun$1.apply(<console>:10) 
    at scala.Option.flatMap(Option.scala:171) 
    ... 33 elided 

scala> val a: Option[Int] = None 
a: Option[Int] = None 

scala> (for (x <- a; y <- b) yield { require(x < y); y - x }).ensuring(_.nonEmpty) 
java.lang.AssertionError: assertion failed 
    at scala.Predef$.assert(Predef.scala:151) 
    at scala.Predef$Ensuring$.ensuring$extension2(Predef.scala:255) 
    ... 33 elided 

Для вашего случая использования, поместите ваши ограничения в обычай применять:

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

case class C(i: Int, j: Int) 
object C { 
    def apply(a: Option[Int] = None, b: Option[Int] = None) = (
    for (x <- a; y <- b) yield { 
     require(x < y, "Order violation") 
     new C(x, y) 
    } 
).ensuring(_.nonEmpty, "Missing value").get 
} 

// Exiting paste mode, now interpreting. 

defined class C 
defined object C 

scala> C(Option(42)) 
java.lang.AssertionError: assertion failed: Missing value 
    at scala.Predef$Ensuring$.ensuring$extension3(Predef.scala:256) 
    at C$.apply(<console>:13) 
    ... 33 elided 

scala> C(Option(42),Option(3)) 
java.lang.IllegalArgumentException: requirement failed: Order violation 
    at scala.Predef$.require(Predef.scala:219) 
    at C$$anonfun$apply$1$$anonfun$apply$2.apply(<console>:12) 
    at C$$anonfun$apply$1$$anonfun$apply$2.apply(<console>:12) 
    at scala.Option.map(Option.scala:146) 
    at C$$anonfun$apply$1.apply(<console>:12) 
    at C$$anonfun$apply$1.apply(<console>:12) 
    at scala.Option.flatMap(Option.scala:171) 
    at C$.apply(<console>:12) 
    ... 33 elided 

scala> C(Option(2),Option(3)) 
res5: C = C(2,3) 

Edit: требуется только один аргумент.

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

case class C(a: Option[Int] = None, b: Option[Int] = None) { 
    require(a orElse b nonEmpty, "No value") 
    for (x <- a; y <- b) require(x < y, "Order violation") 
} 

// Exiting paste mode, now interpreting. 

warning: there was one feature warning; re-run with -feature for details 
defined class C 

scala> C() 
java.lang.IllegalArgumentException: requirement failed: No value 
    at scala.Predef$.require(Predef.scala:219) 
    ... 34 elided 

scala> C(Option(42)) 
res1: C = C(Some(42),None) 

scala> C(Option(42),Option(3)) 
java.lang.IllegalArgumentException: requirement failed: Order violation 
    at scala.Predef$.require(Predef.scala:219) 
    at C$$anonfun$1$$anonfun$apply$mcVI$sp$1.apply$mcVI$sp(<console>:9) 
    at C$$anonfun$1$$anonfun$apply$mcVI$sp$1.apply(<console>:9) 
    at C$$anonfun$1$$anonfun$apply$mcVI$sp$1.apply(<console>:9) 
    at scala.Option.foreach(Option.scala:257) 
    at C$$anonfun$1.apply$mcVI$sp(<console>:9) 
    at C$$anonfun$1.apply(<console>:9) 
    at C$$anonfun$1.apply(<console>:9) 
    at scala.Option.foreach(Option.scala:257) 
    ... 34 elided 

scala> C(Option(2),Option(3)) 
res3: C = C(Some(2),Some(3)) 
+0

Хотя этот ответ - почти правильные строки, я ожидал бы, что C (None, Option (3)) не будет бросать 'java.lang.AssertionError: утверждение не выполнено: отсутствует значение' .. Проблема возникает из-за использования понятий, за которыми следуют .get –

+0

@VenkatSudheerReddyAedama Извините за tldr; это из-за 'обеспечения', на самом деле; Я неправильно понял ваш вопрос. –