All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.dsl.kt Maven / Gradle / Ivy

Go to download

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)
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy