abstract class Interpreter where IS : Interpreter.Intermediary, TS : Interpreter.Terminal, SELF : Interpreter { sealed interface Step where INTERPRETER : Interpreter, out Terminal, INTERPRETER> interface Intermediary : Step where INTERPRETER : Interpreter, out Terminal, INTERPRETER> interface Terminal : Step where INTERPRETER : Interpreter, out Terminal, INTERPRETER> abstract fun next(currentStep: IS): () -> Step } sealed interface BaseTerminal, I>> : Interpreter.Terminal { data class Success, I>>( val result: Int ) : BaseTerminal } class CountingInterpreter : Interpreter, CountingInterpreter>() { sealed interface Intermediary : Interpreter.Intermediary { data class KeepCounting( val togo: Int ) : Intermediary } var count = 0 override fun next( currentStep: Intermediary ): () -> Step = { when (currentStep) { is Intermediary.KeepCounting -> if (currentStep.togo == 0) { BaseTerminal.Success(count) } else { count++ Intermediary.KeepCounting(currentStep.togo - 1) } } } } fun box(): String { val interpreter = CountingInterpreter() var step: Interpreter.Step = CountingInterpreter.Intermediary.KeepCounting(1) while (step is CountingInterpreter.Intermediary) { step = interpreter.next(step).invoke() } return if ((step as BaseTerminal.Success).result == 1) "OK" else "NOK" }