2

I have a function like the following:

fun getCommonItemsFrom(element: Element): List<ElementItem>? =
    if (element.parent is CommonElementWrapper) element.items
    else null

So let's assume that Element has a property called parent, which is of type ElementWrapper? (an interface). And this property may or may not be a concrete instance of CommonElementWrapper.

This function returns the items (that is non-nullable List<ElementItem>) of an Element, as long as the parent property is an instance of CommonElementWrapper, otherwise null will be returned.

So I can use it like this:

if (getCommonItemsFrom(element) == null) {
    return
}

// At this point I can infer that `element.parent` is a `CommonElementWrapper`.
// Since the above condition was not `null`.

if (element.parent.isSomeCommonElementWrapperThing()) {
    // Error: I can't use it this way without first re-checking the parent type.
    // ...
}

But currently I need to double check:

if (element.parent is CommonElementWrapper &&
    element.parent.isSomeCommonElementWrapperThing()) { 
    // ...
}

I was wondering if Kotlin has some way of after a certain function is executed it allows to infer some things from there. Something like:

@InferIfReturnIsNotNull (element.parent is CommonElementWrapper)
fun getCommonItemsFrom(element: Element): List<ElementItem>? = ...
David Rodrigues
  • 12,041
  • 16
  • 62
  • 90
  • 3
    I believe [kotlin contracts](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.contracts/) could probably do this kind of thing, but it's quite experimental. – Sweeper Jul 04 '22 at 20:17
  • 1
    As @Sweeper said, this is provided by contracts. But there is another thing: we can smart casts properties only in specific cases when the compiler can guarantee they are truly immutable. If your above `if` works already, then I guess this is your case. But we have to be aware of this limitation. – broot Jul 04 '22 at 20:59
  • @Sweeper well, after you mentioned the term "contract" it was easier to understand how all this works (and it was pretty much what I needed). However, all I could find is that contracts don't work on properties, only on the own function arguments. https://stackoverflow.com/a/66978893/755393 – David Rodrigues Jul 04 '22 at 21:02
  • 1
    @DavidRodrigues Ahh, right. I tried to hack my way through this by making `parent` property generic, because I don't see a reason why a contract can't guarantee the object is `Element`. Unfortunately, contracts are pretty immature and their DSL rejected my `implies (element is Element)`, saying... `is` can't check for erased types :-D That's pretty silly, but well, this is a new feature. – broot Jul 04 '22 at 22:08

0 Answers0