Solution: Cafeteria simulation

suspend fun main(): Unit = coroutineScope { val orders = Channel<CoffeeType>(Channel.UNLIMITED) val coffees = Channel<Coffee>(Channel.UNLIMITED) serveOrders(orders, coffees, "Alice") serveOrders(orders, coffees, "Bob") serveOrders(orders, coffees, "Celine") serveOrders(orders, coffees, "Dave") launch { for (coffee in coffees) { println("Coffee $coffee is ready") } } println("Welcome to Dream Coffee!") println("Press E to get espresso, L to get latte.") while (true) { val type = when (readlnOrNull()) { "E" -> CoffeeType.ESPRESSO "L" -> CoffeeType.LATTE else -> continue } orders.send(type) println("Order for $type sent") } } fun CoroutineScope.serveOrders( orders: ReceiveChannel<CoffeeType>, servedOrders: SendChannel<Coffee>, baristaName: String ) = launch { for (order in orders) { val coffee = makeCoffee(order, baristaName) servedOrders.send(coffee) } }

Example solution in playground

import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch enum class CoffeeType { ESPRESSO, LATTE } class Milk class GroundCoffee sealed class Coffee class Espresso(ground: GroundCoffee) : Coffee() { override fun toString(): String = "Espresso" } class Latte(milk: Milk, espresso: Espresso) : Coffee() { override fun toString(): String = "Latte" } suspend fun main(): Unit = coroutineScope { val orders = Channel<CoffeeType>(Channel.UNLIMITED) val coffees = Channel<Coffee>(Channel.UNLIMITED) serveOrders(orders, coffees, "Alice") serveOrders(orders, coffees, "Bob") serveOrders(orders, coffees, "Celine") serveOrders(orders, coffees, "Dave") launch { for (coffee in coffees) { println("Coffee $coffee is ready") } } println("Welcome to Dream Coffee!") println("Press E to get espresso, L to get latte.") while (true) { val type = when (readlnOrNull()) { "E" -> CoffeeType.ESPRESSO "L" -> CoffeeType.LATTE else -> continue } orders.send(type) println("Order for $type sent") } } fun CoroutineScope.serveOrders( orders: ReceiveChannel<CoffeeType>, servedOrders: SendChannel<Coffee>, baristaName: String ) = launch { for (order in orders) { val coffee = makeCoffee(order, baristaName) servedOrders.send(coffee) } } private suspend fun makeCoffee(order: CoffeeType, baristaName: String): Coffee { val groundCoffee = groundCoffee(baristaName) val espresso = makeEspresso(groundCoffee, baristaName) return when (order) { CoffeeType.ESPRESSO -> espresso CoffeeType.LATTE -> { val milk = brewMilk(baristaName) Latte(milk, espresso) } } } suspend fun groundCoffee(baristaName: String): GroundCoffee { println("$baristaName: Grinding coffee...") delay(3000) return GroundCoffee() } suspend fun brewMilk(baristaName: String): Milk { println("$baristaName: Brewing milk...") delay(3000) return Milk() } suspend fun makeEspresso(ground: GroundCoffee, baristaName: String): Espresso { println("$baristaName: Making espresso...") delay(3000) return Espresso(ground) }