“A monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.”
B> { data class Failure<out A>(val value: A) : Either<A, Nothing>() data class Success<out B>(val value: B) : Either<Nothing, B>() } Can only be an instance of failure or success
B> { data class Failure<out A>(val value: A) : Either<A, Nothing>() data class Success<out B>(val value: B) : Either<Nothing, B>() } Can only be an instance of failure or success Covariant generic types to represent the left and right compartments
B> { data class Failure<out A>(val value: A) : Either<A, Nothing>() data class Success<out B>(val value: B) : Either<Nothing, B>() } Can only be an instance of failure or success Covariant generic types to represent the left and right compartments Right compartment is empty
B> { data class Failure<out A>(val value: A) : Either<A, Nothing>() data class Success<out B>(val value: B) : Either<Nothing, B>() } Can only be an instance of failure or success Covariant generic types to represent the left and right compartments Right compartment is empty Left compartment is empty
{ val beansEither = grindBeans() return when (beansEither) { is Either.Failure -> beansEither // instance of Failure is Either.Success -> Either.Success(brew(beansEither.value)) } } }
{ val beansEither = grindBeans() return when (beansEither) { is Either.Failure -> beansEither // instance of Failure is Either.Success -> Either.Success(brew(beansEither.value)) } } } Indicate that makeCoffee() can fail
{ val beansEither = grindBeans() return when (beansEither) { is Either.Failure -> beansEither // instance of Failure is Either.Success -> Either.Success(brew(beansEither.value)) } } } Indicate that makeCoffee() can fail Just return the failure
{ val beansEither = grindBeans() return when (beansEither) { is Either.Failure -> beansEither // instance of Failure is Either.Success -> Either.Success(brew(beansEither.value)) } } } Indicate that makeCoffee() can fail Just return the failure Wrap the result of brew(), we need to return an Either
(B) -> C) = when (this) { is Either.Failure -> this is Either.Success -> Either.Success(fn(this.value)) } Takes a function to convert some object of type B to some object of type C
(B) -> C) = when (this) { is Either.Failure -> this is Either.Success -> Either.Success(fn(this.value)) } Takes a function to convert some object of type B to some object of type C Just return the failure
(B) -> C) = when (this) { is Either.Failure -> this is Either.Success -> Either.Success(fn(this.value)) } Takes a function to convert some object of type B to some object of type C Just return the failure Apply function and wrap the result
Either<A, B>.flatMap(fn: (B) -> Either<A, C>) = when (this) { is Either.Failure -> this is Either.Success -> fn(this.value) } Takes a function to convert some object of type B to an Either<A, C>
Either<A, B>.flatMap(fn: (B) -> Either<A, C>) = when (this) { is Either.Failure -> this is Either.Success -> fn(this.value) } Takes a function to convert some object of type B to an Either<A, C> Function returns an either, no need to wrap the result
(Either.Failure<A>) -> Nothing): B = when(this) { is Either.Failure -> fn(this) is Either.Success -> value } Apply function if we are dealing with a failure
(Either.Failure<A>) -> Nothing): B = when(this) { is Either.Failure -> fn(this) is Either.Success -> value } Apply function if we are dealing with a failure Return unboxed value on success
(Either.Failure<A>) -> Nothing): B = when(this) { is Either.Failure -> fn(this) is Either.Success -> value } Apply function if we are dealing with a failure Return unboxed value on success fun makeCoffee(): Either<MachineFailure, Coffee> { val beans = grindBeans().onFailure { return it } val water = boilWater().onFailure { return it } return brew(beans, water) }
(Either.Failure<A>) -> Nothing): B = when(this) { is Either.Failure -> fn(this) is Either.Success -> value } Apply function if we are dealing with a failure Return unboxed value on success fun makeCoffee(): Either<MachineFailure, Coffee> { val beans = grindBeans().onFailure { return it } val water = boilWater().onFailure { return it } return brew(beans, water) } Return from within a lambda out of the enclosing function
Serve coffee on success class Barista(private val machine: CoffeeMachine) { fun handleOrder() { machine.makeCoffee().fold( onSuccess = { serveToCustomer(it) }, onFailure = { when (it) { MachineFailure.MissingFilter -> TODO() MachineFailure.NotEnoughBeans -> TODO() } }) } } When is only exhaustive if it’s used as an expression
an exception to an Either? As close as possible to the function that produces the exception At the boundaries of your application Accessing resources over HTTP
an exception to an Either? As close as possible to the function that produces the exception At the boundaries of your application Accessing resources over HTTP Saving to a database