-
Notifications
You must be signed in to change notification settings - Fork 62
Open
Description
Currently T in Assertion.Builder is invariant. Is there a specific reason for that?
I have stumbled upon a problem with a custom assertion I was writing. I have a structure which is essentially a tree of maps:
data class Node(id: String, children: MutableMap<String, Node>)
fun buildTree(): Map<String, Node> = TODO("irrelevant")I wanted to create two assertions:
fun Assertion.Builder<Map<String, Node>>.node(
id: String,
childrenAssertion: Assertion.Builder<Map<String, Node>>.() -> Unit
) {
withValue(id) {
get { this.id }.isEqualTo(id)
get { children }.childrenAssertion()
}
}
fun Assertion.Builder<Map<String, Node>>.leaf(id: String) {
withValue(id) {
get { this.id }.isEqualTo(id)
get { children }.isEmpty()
}
}
// usage
expectThat(buildTree()) {
node("node1") {
node("node2") {
leaf("leaf1")
}
leaf("leaf2")
leaf("leaf3")
}
}But it didn't work because childrenAssertion binds T to Map<*, *> where get { children } returns builder with T bound to MutableMap<*, *>. The solution was to use call-site projection:
fun Assertion.Builder<out Map<String, Node>>.node(
id: String,
childrenAssertion: Assertion.Builder<out Map<String, Node>>.() -> Unit
)But then I wondered why Assertion.Builder is not covariant in the first place? What would be the drawbacks of defining it as Assertion.Builder<out T>?
Metadata
Metadata
Assignees
Labels
No labels