我有一个在数据库上运行的简单
slick
查询:
def method(): Future[Either[Error, MyCustomDTO]] =
OptionT(database.run(query))
.map(MyCustomDTO(_))
.toRight(dataNotFound())
.value
问题出在
.toRight
。我想将其映射到不同的错误,具体取决于数据库返回的内容。例如
case FOREIGN_KEY_CONSTRAINT_VIOLATION.toString => constraintError()
case UNIQUE_CONSTRAINT_VIOLATION.toString => uniqueError()
case _ => dataNotFound()
我尝试在
match case
中执行.toRight()
,但它不起作用:
.toRight(error => error.asInstanceOf[PSQLException].getSQLState match { .... })
我想知道以正确的方式在这里映射不同错误的最佳可能性是什么?
toRight(...)
的签名
def toRight[L](left: => L)(implicit F: Functor[F]): EitherT[F, L, A] =
EitherT(cata(Left(left), Right.apply))
def toRightF[L](left: => F[L])(implicit F: Monad[F]): EitherT[F, L, A] =
EitherT(cataF(F.map(left)(Left.apply[L, A]), a => F.pure(Right(a))))
这两个参数都采用名称参数 - 换句话说,它们是
() => ...
的特殊语法,其中为您插入定义中的 () => ...
和应用程序中的 ...()
。为什么?因为在 toLeft
中的 toRight
/OptionT
上,假设您正在处理由 Option
表示的错误类型 - 即 None
。由于不需要传递 _: None.type =>
,因此它使用按名称参数。
如果你想处理错误,你必须在
F[A]
内部处理它 - 通过提供正确的类型类(ApplicativeError[F, Throwable]
/MonadError[F, Throwable]
),这将允许调用handleError
/handleErrorWith
/redeem
等
// F: ApplicativeError[F, Throwable]
// fa: F[MyCustomDTO]
F.redeem(fa)(a => Right(a), error => Left(error match { ... }))
// or with extension methods
fa.redeem(a => Right(a), error => Left(error match { ... }))
因为你的
F
似乎是Future
,而且因为你把它包裹在OptionT
中,我想如果你这样做的话效果会更好:
OptionT(database.run(query))
.map(MyCustomDTO(_).asRight[Error])
.getOrElse(dataNotFound().asLeft[MyCustomDTO])
.handleWith { error =>
Left(error.asInstanceOf[PSQLException].getSQLState match {
...
})
}