首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala中的纯和安全的错误转换

Scala中的纯和安全的错误转换
EN

Stack Overflow用户
提问于 2019-02-22 12:19:36
回答 1查看 447关注 0票数 2

我使用cats-effect来挂起副作用,在实现纯函数时遇到了困难,避免了容易出错的Throwable,问题是cats.effect.Sync[F[_]]扩展了Bracket[F, Throwable]

代码语言:javascript
复制
sealed trait Err
    final case class FileExistError(path: String) extends Err
    case object UnknownError extends Err

final case class FileExistThrowable(path: String, cause: Throwable) extends Throwable

final class File[F[_]: Sync]{
    def rename(from: String, to: String): F[Unit] = 
       implicitly[Sync[F]] delay {
           try{
               Files.move(Paths.get(from), Paths.get(to))
           } catch {
               case e: FileAlreadyExistsException =>
                  throw FileExistThrowable(to, e)
               case e => throw e
           }
       }
}

在例如cats.effect.IO的情况下,我可以使用NaturalTransform转换效果,如下所示:

代码语言:javascript
复制
implicit val naturalTransform: IO ~> EitherT[IO, Err, ?] = 
new ~>[IO, EitherT[IO, Err, ?]] {
  override def apply[A](fa: IO[A]): EitherT[IO, Err, A] =
    EitherT(
      fa.attempt map { e =>
        e.left map {
          case FileExistsThrowable(path, cause) => 
               FileExistsError(path)
          case NonFatal(e) =>
               UnknownError
        }
      }
    )
}

不幸的是,这似乎不可靠,而且容易出错。在有效的实现中,我们可以随意抛出任何类型的抛出,这些抛出将被报告为UnknownError

比起简单地将Throwabletry-catch一起使用,这似乎不太可靠。有人能建议一种更好/更安全的方法来处理错误吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-22 16:54:33

当您身处IO这个混浊的世界时,无法摆脱Throwable可能发生的事实。关键是区分真正异常的错误和预期的错误。

这是一个永无止境的追求,试图建立一个可能发生在野外的错误的类型模型,所以我的建议是不要尝试。相反,决定要将哪些错误具体化为API,并允许任何其他错误以Throwables的形式在IO中发生,这样调用方就可以在执行预期错误处理的同时,决定是否和在何处处理异常情况。

您的场景的一个非常简单的示例可以是:

代码语言:javascript
复制
final case class FileAlreadyExists(path: String)

final class File[F[_]: Sync]{
  def rename(from: String, to: String): F[Either[FileAlreadyExists, Unit]] =
    Sync[F].delay { Files.move(Paths.get(from), Paths.get(to))}.attempt.flatMap {
      case Left(_ : FileAlreadyExistsException) => Sync[F].pure(Left(FileAlreadyExists(to)))
      case Left(e)                              => Sync[F].raiseError(e)
      case Right(_)                             => Sync[F].pure(Right(()))
    }
}

通过这种方式,您可以区分将文件重命名为已存在的文件的预期错误(该错误发生在Either中并具有良好的类型)和完全出乎意料的错误(仍然发生在IO中),并可能在其他地方处理。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54827029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档