jrgonzalez
08/01/2022, 8:50 AMjrgonzalez
08/01/2022, 8:58 AMsimon.vergauwen
08/01/2022, 9:01 AMdata class DatabaseError(val error: Throwable)
data class NetworkError(val error: Throwable)
Normally you’d combine them under a common parent using sealed interface MyError
so that you can have Either<MyError, A>
.
But with context receivers you can do:
context(
EffectScope<DatabaseError>,
EffectScope<NetworkError>
)
suspend fun program(): A
This can be good for multiple reasons:
• Doesn’t require inheritance, or common parents. So it allows you to keep clear seperation of concern between errors.
• It allows you to resolve specific errors, or install error handlers for specific types, wherever you want.
I.e. you can resolve DatabaseError
but leave the NetworkError
for another layer.
context(EffectScope<NetworkError>)
suspend fun programOrNull(): A? =
effect<DatabaseError, A> {
program()
}.orNull()
simon.vergauwen
08/01/2022, 9:01 AMjrgonzalez
08/01/2022, 9:05 AMsimon.vergauwen
08/01/2022, 9:06 AMCoproduct
or Union
(abstract common parent
) in the Left
side, but that typically doesn’t result in less boilerplate or cleaner code than if you use a concrete common parent
.jrgonzalez
08/01/2022, 9:10 AMDomainError
as a shared type across compilation modules and just check on subtypes I can handle at the specific effect (or map left to something else). That may be what I would go for since the attempts to fold over not sealed hierarchies is doubtfully better. Some subtypes of DomainError when contained within a single module could still be sealed too, like you do in your webinar code with subtypes of DomainError that are also sealed 👌Anthony Jeong
08/05/2022, 12:56 AM