首页 > 解决方案 > 是否有用于对没有共同祖先的类进行分组的“OneOf”类

问题描述

与此类似,我不想接受几个不相关的类之一,而是想返回一个。

我有一个使用多个底层存储库的编排服务。每个 repo 都可以传回一个错误。但是,这些错误类不共享一个共同的祖先。

例如:

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

根据链接的 SO 答案,我尝试了:

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)

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

标签: scala

解决方案


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本质上,它所做的只是在您调用时检查 -witness是否存在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
}

推荐阅读