Skip to content

Instantly share code, notes, and snippets.

@y2k
Last active February 9, 2019 13:59
Show Gist options
  • Save y2k/6b3b6e926f1e815ca55735665991be2f to your computer and use it in GitHub Desktop.
Save y2k/6b3b6e926f1e815ca55735665991be2f to your computer and use it in GitHub Desktop.
Пример асинхронной программы, без kotlinx.coroutines
package example1
import java.util.concurrent.ThreadLocalRandom
typealias F = SequenceScope<Eff<*>>
sealed class Eff<T> {
class WriteLine(val text: String, val f: (Unit) -> Unit = {}) : Eff<Unit>()
class ReadLine(val f: (String) -> Unit = {}) : Eff<String>()
class Random(val min: Int, val max: Int, val f: (Int) -> Unit = {}) : Eff<Int>()
}
suspend fun F.writeLine(text: String) = await(Eff.WriteLine(text))
suspend fun F.readLine(): String = await(Eff.ReadLine())
suspend fun F.random(min: Int, max: Int): Int = await(Eff.Random(min, max))
suspend fun main() = interpret {
writeLine("Я загадал число от 1 до 10, попробуйте угадать")
val hiddenNumber = random(1, 10)
loop(hiddenNumber)
}
private suspend fun F.loop(hiddenNumber: Int) {
val guess = readNextGuess()
when {
guess > hiddenNumber -> {
writeLine("Ваше число больше загаданного")
loop(hiddenNumber)
}
guess < hiddenNumber -> {
writeLine("Ваше число меньше загаданного")
loop(hiddenNumber)
}
else -> writeLine("Вы угадали!")
}
}
private suspend fun F.readNextGuess(): Int {
writeLine("Введите число:")
return readLine().toIntOrNull() ?: readNextGuess()
}
suspend fun <T> interpret(f: suspend F.() -> T): T {
var result: T? = null
sequence<Eff<*>> { result = f() }.forEach {
kotlinx.coroutines.delay(100) // TODO: для тестирования реальной асинхронности
when (it) {
is Eff.ReadLine -> it.f(readLine()!!)
is Eff.WriteLine -> it.f(println(it.text))
is Eff.Random -> it.f(ThreadLocalRandom.current().nextInt(it.min, it.max + 1))
}
}
return result!!
}
@Suppress("UNCHECKED_CAST")
suspend fun <T> F.await(eff: Eff<T>): T {
var result: T? = null
when (eff) {
is Eff.WriteLine -> yield(Eff.WriteLine(eff.text) { result = it as T })
is Eff.ReadLine -> yield(Eff.ReadLine { result = it as T })
is Eff.Random -> yield(Eff.Random(eff.min, eff.max) { result = it as T })
}
return result!!
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment