使用 scala cat 检查非空字段的组合
Using scala cats to check combination of non-empty fields
在我的代码中我有一个 class 要检查它是否有效我应该评估是否存在至少一个可能的字段组合(存在我的意思是组合的每个字段不应该是空的)。示例:
case class Test( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double])
要“有效”,必须至少存在以下字段组合之一(“a,b,c”,”a,d,e”,”a,f”)
我试图用 scala cats 库来做这件事,但我有点迷路了。任何建议将不胜感激。
如果你想验证它你可以:
case class Test( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double]) {
// "a,b,c","a,d,e","a,f"
def isValid = (a.isDefined && b.isDefined && c.isDefined) ||
(a.isDefined && d.isDefined && e.isDefined) ||
(a.isDefined && f.isDefined)
}
如果你想确保只有定义了其中一个字段才能创建它,你将不得不使用智能构造函数
sealed abstract case class Test private ( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double])
object Test {
def create( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double]): Either[String, Test] =
if ((a.isDefined && b.isDefined && c.isDefined) ||
(a.isDefined && d.isDefined && e.isDefined) ||
(a.isDefined && f.isDefined))
Right(new Test(a, b, c, d, e, f) {})
else
Left("All arguments are empty")
}
或者使用 ADT 确保定义了其中一个字段:
sealed trait Test extends Product with Serializable
object Test {
final case class Case1( a: String
, b: String
, c: String
, d: Option[String]
, e: Option[Double]
, f: Option[Double]) extends Test
final case class Case2( a: String
, b: Option[String]
, c: Option[String]
, d: String
, e: String
, f: Option[Double]) extends Test
final case class Case3( a: String
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Double) extends Test
}
您可以在此处使用猫...但是为了什么?您没有将元组或选项集合组合成单个选项。您不会将 F[Option[X]
换成 Option[F[X]
或相反。没有副作用、映射、遍历、从嵌入在某些上下文中的较小对象构建新对象等。您可以尝试做一些事情,比如
implicit val booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def combine(a: Boolean, b: Boolean) = a && b
def empty = true
}
def isValid = List(a, b, c).foldMap(_.isDefined) ||
List(a, d, e).foldMap(_.isDefined) ||
List(a, f).foldMap(_.isDefined)
甚至可能
def isValid = (
(a, b, c).tupled.void orElse (a, d, e).tupled.void orElse (a, f).tupled.void
).isDefined
但这并不比
好多少
def isValid = List(a, b, c).exists(_.isDefined) ||
List(a, d, e).exists(_.isDefined) ||
List(a, f).exists(_.isDefined)
可在普通 Scala 中实现。我想你可以简单地通过使用 Option[_]
上定义的一些 Ring
来使用 *
和 +
:
implicit val ring: Ring[Option[_]] = ... // yup, existential type here
def isValid = ((a * b * c) + (a * d * e) + (a * f)).isDefined
(这需要 typelevel algebra)但是如果只在一个地方使用它,我看不到增益。
您可以使用 cats *>
运算符组合您感兴趣的字段并测试结果是否仍为非空。 a *> b
是 shorthand 对应 a.flatMap(_ => b)
。
import cats.implicits._
def isValid(t: Test) =
(t.a *> t.b *> t.c).nonEmpty ||
(t.a *> t.d *> t.e).nonEmpty ||
(t.a *> t.f).nonEmpty
但无论哪种方式,您都可以在没有猫的情况下实现与 List(...).forall(_.nonEmpty)
几乎相似的简洁性。
在我的代码中我有一个 class 要检查它是否有效我应该评估是否存在至少一个可能的字段组合(存在我的意思是组合的每个字段不应该是空的)。示例:
case class Test( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double])
要“有效”,必须至少存在以下字段组合之一(“a,b,c”,”a,d,e”,”a,f”)
我试图用 scala cats 库来做这件事,但我有点迷路了。任何建议将不胜感激。
如果你想验证它你可以:
case class Test( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double]) {
// "a,b,c","a,d,e","a,f"
def isValid = (a.isDefined && b.isDefined && c.isDefined) ||
(a.isDefined && d.isDefined && e.isDefined) ||
(a.isDefined && f.isDefined)
}
如果你想确保只有定义了其中一个字段才能创建它,你将不得不使用智能构造函数
sealed abstract case class Test private ( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double])
object Test {
def create( a: Option[String]
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Option[Double]): Either[String, Test] =
if ((a.isDefined && b.isDefined && c.isDefined) ||
(a.isDefined && d.isDefined && e.isDefined) ||
(a.isDefined && f.isDefined))
Right(new Test(a, b, c, d, e, f) {})
else
Left("All arguments are empty")
}
或者使用 ADT 确保定义了其中一个字段:
sealed trait Test extends Product with Serializable
object Test {
final case class Case1( a: String
, b: String
, c: String
, d: Option[String]
, e: Option[Double]
, f: Option[Double]) extends Test
final case class Case2( a: String
, b: Option[String]
, c: Option[String]
, d: String
, e: String
, f: Option[Double]) extends Test
final case class Case3( a: String
, b: Option[String]
, c: Option[String]
, d: Option[String]
, e: Option[Double]
, f: Double) extends Test
}
您可以在此处使用猫...但是为了什么?您没有将元组或选项集合组合成单个选项。您不会将 F[Option[X]
换成 Option[F[X]
或相反。没有副作用、映射、遍历、从嵌入在某些上下文中的较小对象构建新对象等。您可以尝试做一些事情,比如
implicit val booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def combine(a: Boolean, b: Boolean) = a && b
def empty = true
}
def isValid = List(a, b, c).foldMap(_.isDefined) ||
List(a, d, e).foldMap(_.isDefined) ||
List(a, f).foldMap(_.isDefined)
甚至可能
def isValid = (
(a, b, c).tupled.void orElse (a, d, e).tupled.void orElse (a, f).tupled.void
).isDefined
但这并不比
好多少def isValid = List(a, b, c).exists(_.isDefined) ||
List(a, d, e).exists(_.isDefined) ||
List(a, f).exists(_.isDefined)
可在普通 Scala 中实现。我想你可以简单地通过使用 Option[_]
上定义的一些 Ring
来使用 *
和 +
:
implicit val ring: Ring[Option[_]] = ... // yup, existential type here
def isValid = ((a * b * c) + (a * d * e) + (a * f)).isDefined
(这需要 typelevel algebra)但是如果只在一个地方使用它,我看不到增益。
您可以使用 cats *>
运算符组合您感兴趣的字段并测试结果是否仍为非空。 a *> b
是 shorthand 对应 a.flatMap(_ => b)
。
import cats.implicits._
def isValid(t: Test) =
(t.a *> t.b *> t.c).nonEmpty ||
(t.a *> t.d *> t.e).nonEmpty ||
(t.a *> t.f).nonEmpty
但无论哪种方式,您都可以在没有猫的情况下实现与 List(...).forall(_.nonEmpty)
几乎相似的简洁性。