代码之家  ›  专栏  ›  技术社区  ›  St.Antario

Scala中一种纯安全的错误转换方法

  •  0
  • St.Antario  · 技术社区  · 7 年前

    我在用 cats-effect Throwable s、 问题是 cats.effect.Sync[F[_]] 延伸 Bracket[F, Throwable] .

    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转换效果,如下所示:

    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 .

    这似乎并不比简单地使用更可靠 可抛出 try-catch . 有谁能提出更好的处理错误的建议?

    0 回复  |  直到 7 年前
        1
  •  1
  •   TheInnerLight    7 年前

    当你在黑暗的世界里 IO ,这是无法回避的事实 Throwable 可能会发生。关键是要区分真正的错误 异常 以及那些 预期 .

    这是一个永无止境的探索,试图建立一个类型化的模型,可能会发生在野外,所以我的建议是不要尝试。相反,决定要具体化到API中的错误,并允许其他错误作为 Throwables

    一个非常简单的例子可以是:

    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 和完全意外的错误(仍然发生在 )也有可能在其他地方处理。

    推荐文章