Sorry that I’m giving a longer example, but I think it becomes clearer what I want to do:
fun interface Pattern<in A> {
fun test(obj: A): Boolean
}
class Matcher<A, R>(private val obj: A) {
private var result: R? = null
fun otherwise(default: () -> R) = result ?: default()
infix fun Pattern<A>.then(value: () -> R) {
if (result == null && this.test(obj)) {
result = value()
}
}
}
fun <A, R> match(obj: A, body: Matcher<A, R>.() -> R): R =
Matcher<A, R>(obj).run(body)
fun even() = Pattern<Int> { it % 2 == 0 }
fun <A> notNull() = Pattern<A> { it != null }
fun <A, B> pair(a: Pattern<A>, b: Pattern<B>) = Pattern<Pair<A, B>> {
a.test(it.first) && b.test(it.second)
}
fun <A, B> pair(a: A, b: B) = Pattern<Pair<A, B>> {
a == it.first && b == it.second
}
fun main() {
val obj: Pair<String?, Int> = "Sam" to 24
val result = match(obj) {
pair(notNull<String?>(), even()) then { "yes" }
pair("x", 14) then { "oops" } //doesn't compile
otherwise { "no" }
}
}
So, I have two problems here, maybe related: First one is how to get proper type inference in case of notNull()
, so that I don’t need to specify the type (here String?
) explicitly? And the second, more important one is that the second line with then
doesn’t compile, because the required type is Pair<String?, Int>
, but the type for the given pattern is apparently inferred as Pair<String, Int>
. I think it boils down to the line infix fun Pattern<A>.then(value: () -> R)
, where I need instead of A
something like “supertype of A”, but I don’t know how to express this.