Skip to content

Instantly share code, notes, and snippets.

@monzee
Last active March 9, 2017 10:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monzee/6b50368758788b50b16bd126e6774c17 to your computer and use it in GitHub Desktop.
Save monzee/6b50368758788b50b16bd126e6774c17 to your computer and use it in GitHub Desktop.
Monadic comprehension in Kotlin via suspending blocks
import kotlin.coroutines.experimental.*
fun main(vararg args: String) {
safeDiv(None(), Some(1)).let(::println)
safeDiv(Some(5L), Some(0.5)).let(::println)
safeDiv(Some(1), None()).let(::println)
for (x in 20..30 step 5) {
for (y in 15 downTo 0 step 5) {
print("$x / $y = ")
safeDiv(Some(x), Some(y)).let {
when (it) {
is None -> "none"
is Some -> "some:${it.value}"
}
}.let(::println)
}
}
}
fun safeDiv(n: Option<Number>, d: Option<Number>) = Option<Float> {
val divisor = d.extract()
guard { divisor != 0 }
(n or pure(0)) fmap { it.toFloat() / divisor.toFloat() }
}
sealed class Option<T> {
abstract suspend fun extract(): T
abstract infix fun or(alt: Option<T>): Option<T>
suspend infix fun <U> fmap(t2u: (T) -> U): Option<U> = pure(t2u(extract()))
companion object {
operator fun <T> invoke(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend Option.Companion.() -> Option<T>
): Option<T> {
var o: Option<T>? = null
block.startCoroutine(this, object : Continuation<Option<T>> {
override val context = context
override fun resume(value: Option<T>) {
o = value
}
override fun resumeWithException(error: Throwable) {
throw error
}
})
return o ?: empty()
}
suspend fun guard(pred: () -> Boolean) {
suspendCoroutine<Unit> { if (pred()) it.resume(Unit) }
}
fun <T> empty(): Option<T> = None()
fun <T> pure(t: T): Option<T> = Some(t)
suspend infix fun <T, U> Option<(T) -> U>.ap(ot: Option<T>): Option<U> =
ot fmap extract()
}
}
class None<T> : Option<T>() {
override suspend fun extract(): T = suspendCoroutine<T> {}
override fun or(alt: Option<T>) = alt
}
data class Some<T>(val value: T) : Option<T>() {
override suspend fun extract(): T = suspendCoroutine<T> { it.resume(value) }
override fun or(alt: Option<T>) = this
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment