使用`Option`Felds清理`case class`

Kev*_*ith 9 scala shapeless

鉴于:

case class Foo(a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])
Run Code Online (Sandbox Code Playgroud)

我想只允许构造一个Foo 只有至少一个参数Some,即不是所有字段都是None.

编写代数数据类型,然后为每个变体创建子类将是相当多的代码:

sealed trait Foo
case class HasAOnly(a: Int)      extends Foo
case class HasAB(a: Int, b: Int) extends Foo
// etc...
Run Code Online (Sandbox Code Playgroud)

是否有更清洁,即更少的代码,以解决我的问题使用shapeless

Tra*_*own 6

你可以用嵌套的Iors 做这样的事情:

import cats.data.Ior

case class Foo(iors: Ior[Ior[Int, Int], Ior[Int, Int]]) {
  def a: Option[Int] = iors.left.flatMap(_.left)
  def b: Option[Int] = iors.left.flatMap(_.right)
  def c: Option[Int] = iors.right.flatMap(_.left)
  def d: Option[Int] = iors.right.flatMap(_.right)
}
Run Code Online (Sandbox Code Playgroud)

现在不可能Foo用所有Nones 构建一个.您还可以将case类构造函数设置为private,并使Ior逻辑发生在伴随对象上的替代构造函数中,这将使模式匹配更好一些,但它也会使示例更长一些.

不幸的是,这种使用很笨拙.你真正想要的是一种概括,Ior就像shapeless.Coproduct一般化一样Either.不过,我个人并不知道这样的现成版本.


Yaw*_*war 5

感谢sealed abstract case classRob Norris最近公布的技巧,你可以保留你的Foocase类的特性,但也提供你自己的智能构造函数,它Option[Foo]根据给定的参数是否通过你的所有标准来返回:

sealed abstract case class Foo(
  a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])

object Foo {
  private class Impl(
    a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])
    extends Foo(a, b, c, d)

  def apply(
    a: Option[Int],
    b: Option[Int],
    c: Option[Int],
    d: Option[Int]): Option[Foo] =
    (a, b, c, d) match {
      case (None, None, None, None) => None
      case _ => Some(new Impl(a, b, c, d))
    }
}
Run Code Online (Sandbox Code Playgroud)