package com.packt.chapter5 sealed class Either { fun fold(lfn: (L) -> T, rfn: (R) -> T): T = when (this) { is Left -> lfn(this.value) is Right -> rfn(this.value) } fun leftProjection(): Projection = when (this) { is Left -> ValueProjection(this.value) is Right -> EmptyProjection() } fun isLeft() = when (this) { is Left -> true is Right -> false } fun rightProjection(): Projection = when (this) { is Left -> EmptyProjection() is Right -> ValueProjection(this.value) } fun isRight() = when (this) { is Left -> false is Right -> true } } class Left(val value: L) : Either() class Right(val value: R) : Either() fun Projection.getOrElse(or: () -> T): T = when (this) { is EmptyProjection -> or() is ValueProjection -> this.value } sealed class Projection { abstract fun map(fn: (T) -> U): Projection abstract fun exists(fn: (T) -> Boolean): Boolean abstract fun filter(fn: (T) -> Boolean): Projection abstract fun toList(): List abstract fun orNull(): T? } class EmptyProjection : Projection() { override fun map(fn: (T) -> U): Projection = EmptyProjection() override fun exists(fn: (T) -> Boolean): Boolean = false override fun filter(fn: (T) -> Boolean): Projection = this override fun toList(): List = emptyList() override fun orNull(): T? = null } class ValueProjection(val value: T) : Projection() { override fun map(fn: (T) -> U): Projection = ValueProjection(fn(value)) override fun exists(fn: (T) -> Boolean): Boolean = fn(value) override fun filter(fn: (T) -> Boolean): Projection = when (fn(value)) { true -> this false -> EmptyProjection() } override fun toList(): List = listOf(value) override fun orNull(): T? = value } fun eitherExamples() { class User(val name: String, val admin: Boolean) class ServiceAccount class Address(val town: String, val postcode: String) fun getCurrentUser(): Either = TODO() fun getUserAddresses(user: User): List
= TODO() val addresses = getCurrentUser().fold({ emptyList
() }, { getUserAddresses(it) }) val postcodes = getCurrentUser().rightProjection().map { getUserAddresses(it) }.map { addresses.map { it.postcode } }.getOrElse { emptyList() } val service: ServiceAccount? = getCurrentUser().leftProjection().orNull() val usersWithMultipleAddresses = getCurrentUser().rightProjection().filter { getUserAddresses(it).size > 1 } val isAdmin = getCurrentUser().rightProjection().exists { it.admin } }