编码三种方式包含 – 或在Scala中键入

我有一些由三个字段组成的数据,比如说String,Int和Double.

它们都是可选的,但整个事物必须至少包含其中一个字段.

我试过cats.data.Ior,比如MyType = String Ior Int Ior Double.它有效,但实际上用它做东西时感觉有点笨拙.创建一个包含三个Options的case类并在构造函数中抛出异常(如果它们都是None)也应该有用,但我不喜欢这个想法.

我很想知道是否有其他方法可以做到这一点(最好是通过仅在类似Ior的类型中编码整个信息,但复杂性较低,以便使用类型的值不那么复杂).建议您使用任何其他图书馆.

最佳答案 你可能也想过这个想法,但是这样的事情怎么样:

class StringOrIntOrDouble private(val first: Option[String], val second: Option[Int], val third: Option[Double]) {
  def this(first: String) = this(Some(first), None, None)

  def this(second: Int) = this(None, Some(second), None)

  def this(third: Double) = this(None, None, Some(third))

  def this(first: String, second: Int) = this(Some(first), Some(second), None)

  def this(second: Int, third: Double) = this(None, Some(second), Some(third))

  def this(first: String, third: Double) = this(Some(first), None, Some(third))

  def this(first: String, second: Int, third: Double) = this(Some(first), Some(second), Some(third))

}

主构造函数是私有的,因此您无法创建空实例.显然,您可以使用Product和equals,hashCode,toString以及其他有用的东西(如案例类)来实现扩展它(但要注意可能轻易破坏不变量的副本).

不幸的是,如果你想要一个通用版本(或者你在两个地方有相同的类型),你必须将所有“子集”构造函数移动到一个配对对象的命名方法,否则它将因为类型擦除而无法编译.

class TripleIor[+A, +B, +C] private(val first: Option[A], val second: Option[B], val third: Option[C]) {

}

object TripleIor {
  def first[A](first: A) = new TripleIor[A, Nothing, Nothing](Some(first), None, None)

  def second[B](second: B) = new TripleIor[Nothing, B, Nothing](None, Some(second), None)

  def third[C](third: C) = new TripleIor[Nothing, Nothing, C](None, None, Some(third))

  def firstAndSecond[A, B](first: A, second: B) = new TripleIor[A, B, Nothing](Some(first), Some(second), None)

  def secondAndThird[B, C](second: B, third: C) = new TripleIor[Nothing, B, C](None, Some(second), Some(third))

  def firstAndThird[A, C](first: A, third: C) = new TripleIor[A, Nothing, C](Some(first), None, Some(third))

  def apply[A, B, C](first: A, second: B, third: C) = new TripleIor[A, B, C](Some(first), Some(second), Some(third))
}

一种更激进的方式是实现与密封特征和7个子类相同的想法,但我认为它们不会更容易使用.此外,它还允许您以类型安全的方式实现复制,但需要大量输入的成本(此处未显示).

sealed trait TripleIor[A, B, C] extends Product {
  def firstOption: Option[A]

  def secondOption: Option[B]

  def thirdOption: Option[C]
}

object TripleIor {

  final case class First[A](first: A) extends TripleIor[A, Nothing, Nothing] {
    override def firstOption: Option[A] = Some(first)

    override def secondOption: Option[Nothing] = None

    override def thirdOption: Option[Nothing] = None
  }

  final case class Second[B](second: B) extends TripleIor[Nothing, B, Nothing] {
    override def firstOption: Option[Nothing] = None

    override def secondOption: Option[B] = Some(second)

    override def thirdOption: Option[Nothing] = None
  }

  final case class Third[C](third: C) extends TripleIor[Nothing, Nothing, C] {
    override def firstOption: Option[Nothing] = None

    override def secondOption: Option[Nothing] = None

    override def thirdOption: Option[C] = Some(third)
  }

  final case class FirstSecond[A, B](first: A, second: B) extends TripleIor[A, B, Nothing] {
    override def firstOption: Option[A] = Some(first)

    override def secondOption: Option[B] = Some(second)

    override def thirdOption: Option[Nothing] = None
  }

  final case class SecondThird[B, C](second: B, third: C) extends TripleIor[Nothing, B, C] {
    override def firstOption: Option[Nothing] = None

    override def secondOption: Option[B] = Some(second)

    override def thirdOption: Option[C] = Some(third)
  }

  final case class FirstThird[A, C](first: A, third: C) extends TripleIor[A, Nothing, C] {
    override def firstOption: Option[A] = Some(first)

    override def secondOption: Option[Nothing] = None

    override def thirdOption: Option[C] = Some(third)
  }

  final case class All[A, B, C](first: A, second: B, third: C) extends TripleIor[A, B, C] {
    override def firstOption: Option[A] = Some(first)

    override def secondOption: Option[B] = Some(second)

    override def thirdOption: Option[C] = Some(third)
  }
}

附:请注意,这里的所有示例都只是草图,以说明不实现许多有用甚至是必需的东西的想法

点赞