是否有一个`OneOf`类用于对没有共同祖先的类进行分组

时间:2018-06-29 22:43:30

标签: scala

Similar to this,我不想返回几个不相关的类之一,而是想返回一个。

我有一个利用几个基础存储库的协调服务。每个回购可以传回错误。但是,这些错误类并不具有相同的祖先。

例如:

case class DistributionError
case class UserError
case class ContentError

我想像这样创建我的编排服务方法:

def doSomethingComplicated(): Either[TheErrors, Boolean] = {

//...
  case Failure(e) => Left(new DistributionError)
//...
}

然后我将这样调用它:

doSomethingComplicated() match {
   case Left(error) => {
      error match {
        case _: DistributionError => ...
        case _: UserError => ...
        case _: ContentError => ...
      }
   }
   case Right(success) => ...
}

As per the linked SO answer,我尝试过:

class TheErrors[T]
object TheErrors {
  implicit object DistWitness extends TheErrors[DistributionError]
  implicit object UserWitness extends TheErrors[UserError]
  implicit object ContentWitness extends TheErrors[ContentError]
}

但是它只是不能像处理参数一样工作。编译器总是抱怨:

> Error:(176, 48) type mismatch;  
>    found   : UserError
>    required: T
>             case None => Left(UserError)

甚至可以将这种方法用于返回类型吗?

2 个答案:

答案 0 :(得分:1)

AnyRef解决方案

一种快速而廉价的解决方案是删除整个TheErrors类型类,然后从Either[AnyRef, Boolean]返回doSomethingComplicated

现有类型解决方案

如果您绝对要确保doSomethingComplicated仅返回先前在TheErrors随播对象中明确列出的错误类型,则可以执行以下操作:

import scala.language.existentials

case class DistributionError()
case class UserError()
case class ContentError()

class TheErrors[T]
object TheErrors {
  implicit object DistWitness extends TheErrors[DistributionError]
  implicit object UserWitness extends TheErrors[UserError]
  implicit object ContentWitness extends TheErrors[ContentError]
}

def allowedError[E](e: E)(implicit witness: TheErrors[E])
: (E, TheErrors[E]) = (e, witness)

type AllowedError = (E, TheErrors[E]) forSome { type E }

def doSomethingComplicated(): Either[AllowedError, Boolean] = {
  import TheErrors._
  /* sth complicated */ Left(allowedError(DistributionError()))
}

doSomethingComplicated() match {
   case Left((error, _)) => {
      error match {
        case _: DistributionError => 42
        case _: UserError => 58
        case _: ContentError => 100
      }
   }
   case Right(success) => 2345678
}

本质上,它要做的就是在您致电TheErrors时检查是否存在allowedError证人,并将见证人附加到错误上。这样可以确保仅从doSomethingComplicated返回可以找到证人的错误。但是请注意,它并不能帮助您检查模式匹配的详尽性。为此,您将必须采用通常的方法,并将所有错误包装到一个常见的密封特征的子类中。

密封性状解决方案

import scala.language.implicitConversions

case class DistributionError()
case class UserError()
case class ContentError()

sealed trait TheErrors
case class Distr(e: DistributionError) extends TheErrors
case class User(e: UserError) extends TheErrors
case class Content(e: ContentError) extends TheErrors

object TheErrors {
  implicit def apply(d: DistributionError): TheErrors = Distr(d)
  implicit def apply(d: UserError): TheErrors = User(d)
  implicit def apply(d: ContentError): TheErrors = Content(d)
}

def doSomethingComplicated(): Either[TheErrors, Boolean] = {
  /* sth complicated */ Left(DistributionError())
}

doSomethingComplicated() match {
   case Left(error) => {
      error match {
        case Distr(e) => 42
        case User(e) => 58
        case Content(e) => 100
      }
   }
   case Right(success) => 2345678
}

隐式转换+普通的旧子类多态性

借助隐式转换和良好的旧子类多态性,您可以摆脱调用者代码中TheErrors 中任何特定的doSomethingComplicated子类:

import scala.language.implicitConversions

case class DistributionError()
case class UserError()
case class ContentError()

sealed trait TheErrors {
  def error: AnyRef
}

object TheErrors {
  private case class TheError(val error: AnyRef) extends TheErrors
  implicit def apply(d: DistributionError): TheErrors = TheError(d)
  implicit def apply(d: UserError): TheErrors = TheError(d)
  implicit def apply(d: ContentError): TheErrors = TheError(d)
}

def doSomethingComplicated(): Either[TheErrors, Boolean] = {
  /* sth complicated */ Left(DistributionError())
}

doSomethingComplicated() match {
   case Left(e) => {
      e.error match {
        case _: DistributionError => 42
        case _: UserError => 58
        case _: ContentError => 100
      }
   }
   case Right(success) => 2345678
}

答案 1 :(得分:1)

很少有选择:

  • 使用Shapeless's Coproduct type
  • 使用Any
  • 嵌套的EitherEither[Either[Either[A, B], C], D]
  • 使用仅包装其对应类型(即class UserErrorWrapper(val u: UserError) extends MyErrorTrait
  • 的子类)定义自己的密封特征
  • 等待Dotty并使用union type
相关问题