Last active
March 9, 2017 10:54
-
-
Save monzee/6b50368758788b50b16bd126e6774c17 to your computer and use it in GitHub Desktop.
Monadic comprehension in Kotlin via suspending blocks
This file contains 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
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