// version 1.2.41 import kotlin.coroutines.experimental.* import kotlin.coroutines.experimental.intrinsics.* fun main(args: Array) = amb { val a = amb("the", "that", "a") val b = amb("frog", "elephant", "thing") val c = amb("walked", "treaded", "grows") val d = amb("slowly", "quickly") if (a[a.lastIndex] != b[0]) amb() if (b[b.lastIndex] != c[0]) amb() if (c[c.lastIndex] != d[0]) amb() println(listOf(a, b, c, d)) val x = amb(1, 2, 3) val y = amb(7, 6, 4, 5) if (x * y != 8) amb() println(listOf(x, y)) } class AmbException(): Exception("Refusing to execute") data class AmbPair(val cont: Continuation, val valuesLeft: MutableList) @RestrictsSuspension class AmbEnvironment { val ambList = mutableListOf>() suspend fun amb(value: T, vararg rest: T): T = suspendCoroutineOrReturn { cont -> if (rest.size > 0) { ambList.add(AmbPair(clone(cont), mutableListOf(*rest))) } value } suspend fun amb(): Nothing = suspendCoroutine { } } @Suppress("UNCHECKED_CAST") fun amb(block: suspend AmbEnvironment.() -> R): R { var result: R? = null var toThrow: Throwable? = null val dist = AmbEnvironment() block.startCoroutine(receiver = dist, completion = object : Continuation { override val context: CoroutineContext get() = EmptyCoroutineContext override fun resume(value: R) { result = value } override fun resumeWithException(exception: Throwable) { toThrow = exception } }) while (result == null && toThrow == null && !dist.ambList.isEmpty()) { val last = dist.ambList.run { this[lastIndex] } if (last.valuesLeft.size == 1) { dist.ambList.removeAt(dist.ambList.lastIndex) last.apply { (cont as Continuation).resume(valuesLeft[0]) } } else { val value = last.valuesLeft.removeAt(last.valuesLeft.lastIndex) (clone(last.cont) as Continuation).resume(value) } } if (toThrow != null) { throw toThrow!! } else if (result != null) { return result!! } else { throw AmbException() } } val UNSAFE = Class.forName("sun.misc.Unsafe") .getDeclaredField("theUnsafe") .apply { isAccessible = true } .get(null) as sun.misc.Unsafe @Suppress("UNCHECKED_CAST") fun clone(obj: T): T { val clazz = obj::class.java val copy = UNSAFE.allocateInstance(clazz) as T copyDeclaredFields(obj, copy, clazz) return copy } tailrec fun copyDeclaredFields(obj: T, copy: T, clazz: Class) { for (field in clazz.declaredFields) { field.isAccessible = true val v = field.get(obj) field.set(copy, if (v === obj) copy else v) } val superclass = clazz.superclass if (superclass != null) copyDeclaredFields(obj, copy, superclass) }