kotlinadvanced

Functional Error Handling with Either

Implement Either monad for type-safe error handling with map, flatMap, and fold operations.

kotlin
sealed class Either<out L, out R> {
    data class Left<L>(val value: L) : Either<L, Nothing>()
    data class Right<R>(val value: R) : Either<Nothing, R>()

    fun <T> map(f: (R) -> T): Either<L, T> = when (this) {
        is Left -> this
        is Right -> Right(f(value))
    }

    fun <T> flatMap(f: (R) -> Either<L, T>): Either<L, T> = when (this) {
        is Left -> this
        is Right -> f(value)
    }

    fun <T> fold(onLeft: (L) -> T, onRight: (R) -> T): T = when (this) {
        is Left -> onLeft(value)
        is Right -> onRight(value)
    }
}

sealed class AppError(val message: String) {
    class NotFound(msg: String) : AppError(msg)
    class Validation(msg: String) : AppError(msg)
}

data class User(val id: Int, val name: String, val email: String)

object UserService {
    private val users = mapOf(1 to User(1, "Alice", "alice@test.com"))

    fun findById(id: Int): Either<AppError, User> =
        users[id]?.let { Either.Right(it) }
            ?: Either.Left(AppError.NotFound("User $id not found"))

    fun validate(user: User): Either<AppError, User> =
        if (user.email.contains("@")) Either.Right(user)
        else Either.Left(AppError.Validation("Invalid email"))
}

fun main() {
    val result = UserService.findById(1).flatMap { UserService.validate(it) }
    println(result.fold({ it.message }, { it.name }))
}

Use Cases

  • Type-safe error handling without exceptions
  • Railway-oriented programming
  • Composable validation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.