Last active
February 9, 2019 13:59
-
-
Save y2k/6b3b6e926f1e815ca55735665991be2f to your computer and use it in GitHub Desktop.
Пример асинхронной программы, без kotlinx.coroutines
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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