jvmMain.dsl.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kontinuity-jvm Show documentation
Show all versions of kontinuity-jvm Show documentation
Provides fully-fledged multishot delimitied continuations in Kotlin with Coroutines
The newest version!
@file:Suppress("CONTEXT_RECEIVERS_DEPRECATED")
import arrow.core.raise.Raise
import arrow.core.raise.SingletonRaise
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.*
/** MonadFail-style errors */
private class PromptFail(private val prompt: Prompt, private val failValue: R) : Raise {
override fun raise(e: Unit): Nothing = prompt.abort(failValue)
}
public typealias Choose = Prompt
public suspend fun Choose.pushChoice(body: suspend () -> R, handler: suspend (R) -> Unit) {
pushPrompt {
handler(body())
}
}
public suspend fun runChoice(
body: suspend context(SingletonRaise, Choose) () -> R, handler: suspend (R) -> Unit
) {
val prompt = Prompt()
prompt.pushChoice({
body(SingletonRaise(PromptFail(prompt, Unit)), prompt)
}, handler)
}
public suspend fun Choose.pushList(body: suspend () -> R): List =
runReader(mutableListOf(), MutableList::toMutableList) {
pushChoice(body) {
ask().add(it)
}
ask()
}
public suspend fun runList(body: suspend context(SingletonRaise, Choose) () -> R): List =
runReader(mutableListOf(), MutableList::toMutableList) {
runChoice(body) {
ask().add(it)
}
ask()
}
public suspend fun listReset(body: suspend context(SingletonRaise, Choose) () -> R): List =
runCC { runList(body) }
context(Choose)
public suspend fun List.bind(): T = shift { continuation ->
for (item in 0..lastIndex) continuation(this[item])
}
public suspend fun Choose.choose(left: T, right: T): T = shift { continuation ->
continuation(left)
continuation(right)
}
context(Choose)
public suspend fun IntRange.bind(): Int = shift { continuation ->
for (i in start..endInclusive) continuation(i)
}
public suspend fun replicate(amount: Int, producer: suspend (Int) -> T): List = runList {
producer((0.. flowReset(
body: suspend context(SingletonRaise, Choose) () -> R
): Flow = channelFlow {
runCC {
runChoice(body, this::send)
}
}
context(Choose)
@OptIn(ExperimentalCoroutinesApi::class)
public suspend fun Flow.bind(): T = shift { continuation ->
// TODO using coroutineScope in such a way is generally unsafe unless a nonReentrant block is used
// inside of it. That's because we can't "see through" the coroutineScope and because it's stateful
coroutineScope {
nonReentrant {
produceIn(this).consumeEach { item ->
continuation(item)
}
}
}
}