- Scala Error Handling의 기본 컨셉은 선언적 처리(Declarative Error Handling)으로 기존 try-catch의 명시적인 방식(Imperative Error Handling)과는 방식이 다르다.
- 선언적 에러 처리의 장점은 아래와 같다.
- 기존 try-catch 방식은 경우에 따라 error의 발생 원인과 try-catch로 인한 로직의 변경 추적이 어렵다. 반면 선언적 처리는 참조 무결성(referentially transparent)이 보장된다.
- Type-safety : 함수의 정의시 return type 뿐만 아니라 어떤 type의 error로 실패하는지 알 수 있게 되어 compile time에 type-safety에 대한 점검이 가능하다.
- Exhaustivity Checking(에러 체크의 완전성) : 컴파일 타임에 반드시 처리해야 하는 error handling 여부에 대한 검사가 가능하다.
- Error Model : ZIO는 Exit, Cause와 같은 에러의 정보를 담고있는 모델을 자체적으로 제공한다. 에러의 유실을 막을 수 있다.
try {
try throw new Error("e1")
finally throw new Error("e2")
} catch {
case e: Error => println(e)
}
// Output:
// e2
---
ZIO.fail("e1")
.ensuring(ZIO.succeed(throw new Exception("e2")))
.catchAll {
case "e1" => Console.printLine("e1")
case "e2" => Console.printLine("e2")
}
// Output:
// e1
- ZIO 전용의 Error를 저장 관리하기 위한 객체인 Cause에 대한 이해가 필수적이다.
- https://zio.dev/reference/core/cause
- It allows us to take a base type E that represents the error type and then capture the sequential and parallel composition of errors in a fully lossless fashion.
- (번역) Cause는 에러(Exception)의 class type을 나타내는 E를 획득할 수 있게 하고, 에러의 누락 방직 차원에서 순차적인 혹은 병렬적인 에러의 포착을 제공한다.
- Cause의 내부 구조는 다음과 같다
- Empty : Cause의 초기상태로 ZIO.succeed(5)와 같이 error가 없는 상태를 의미한다.
- Fail : expected error의 type E를 의미한다.
- Die : defect(unexpected error)의 type E를 의미한다.
- Iterrupt : fiber 등의 멀티 쓰레드 환경에서 interruption을 의미한다.
- Stackless : stack trace 와 excution trace 정보를 담고 있다. (stack trace의 노출 레벨이 Die와는 다르다)
- Both : 병렬 프로그램밍 상에서 2개 이상의 error가 발생하였을때의 정보를 담고 있다.
- Then : 순차적 에러가 발생했을때 Error 객체 저장(고전 try-catch 모델 혹은 fail.ensuring 등)
sealed abstract class Cause[+E] extends Product with Serializable { self =>
import Cause._
def trace: Trace = ???
final def ++[E1 >: E](that: Cause[E1]): Cause[E1] = Then(self, that)
final def &&[E1 >: E](that: Cause[E1]): Cause[E1] = Both(self, that)
}
object Cause extends Serializable {
case object Empty extends Cause[Nothing]
final case class Fail[+E](value: E, override val trace: Trace) extends Cause[E]
final case class Die(value: Throwable, override val trace: Trace) extends Cause[Nothing]
final case class Interrupt(fiberId: FiberId, override val trace: Trace) extends Cause[Nothing]
final case class Stackless[+E](cause: Cause[E], stackless: Boolean) extends Cause[E]
final case class Then[+E](left: Cause[E], right: Cause[E]) extends Cause[E]
final case class Both[+E](left: Cause[E], right: Cause[E]) extends Cause[E]
}
- ZIO상에서 에러의 처리를 위해서는 아래의 관련 함수를 숙지해야 한다.
- .catchAll
- .catchSome
- .either / .absolve
- [R, E, A] --> [R, Either[E, A]]
- [R, Nothing, Either[E, A]] --> [R, E, A]
- .absorb // defect to Failures (recover from both Die and Interruption)
- [Any, Nothing, Nothing] --> [Any, Throwable, Nothing]
- .resurrent // defect to Failures (recover from only from Die)
- [Any, Nothing, Nothing] --> [Any, Throwable, Nothing]
- .orDie
- [R, Throwable, A] --> [R, Nothing, A]
- .refineOrDie // defect to Failures (narraw down the type of the error channel from E)
- [R, Throwable, A] --> [R, IOException, A]
- .unrefine // defect to Failures (broadens the type of the error channel from E to the E1 and embeds some defects into it)
- [R, Nothing, A] --> [R, E, A]
- .sandbox (.unsandbox) // defect to Failures
- [R, Nothing, A] --> [R, Cause[E], A]
- .ensuring : catch block과 유사한 역할
- [R, E, A] --> [R, ???, A]
- .some
- [R, E, Option[A]] --> [R, Option[E], A]
- .cause (.uncause)
- [R, E, A] --> [R, Nothing, Cause[E]]
- .merge
- [R, E, A] --> [R, super(A|E)]
- .reject : success chanel의 값의 일부를 fail channel로 처리
- [R, E, A] --> [R, E, A]
'Tech > ZIO' 카테고리의 다른 글
Scala ZIO의 error handling - (1) (0) | 2024.05.05 |
---|