Support Guard Control like swift


#1

sometimes we have define the option type such as “var a: ClassA? = null” ,and then i need check a :

val realA = a?:return 
if(realA.somePr == true){
//do ....
}
//....

.but i want to use it like swift :

**guard** let realA = a else {
     return
}
if(realA.somePr == true){
//do ....
}
//....

how to impl it?

as i know swift :

guard let unwrappedString = wrappedString ,let bunwrappedString = bwrappedString where .....else {
  return
} // unwrappedString can be accessed outside guard block

kotlin

val unwrappedString = wrappedString?.let { it } ?: return
// unwrappedString is accessible outside this block 

but not powerful


Feature request - guard from Swift
#2

inline infix fun T.or(orBlock: () -> T) = if (this == null) orBlock() else this


#3

The “inline” keyword did the trick. Thanks!

Solution (doesn’t have ‘let’ semantics but at least has control flow):

inline fun guard( condition: Boolean, body: () -> Void ){
	if( !condition )
		body()
}

#4

I came up with this:

fun guard(predicate: () -> Boolean) : Guard {
    return Guard(predicate)
}

data class Guard(val predicate: () -> Boolean) {
    infix fun orElse(trap: () -> Nothing) {
        if (!predicate()) {
            trap()
        }
        // can we assert as post-condition that `predicate` holds?
    }
}

Note “return” type Nothing which is to imitate the semantics of Swift’s guard (which are not allowed to fall through).

Syntactically, this comes close (no assignment, of course):

fun foo(a: Int?) {
    guard { a != null } orElse {
        return@foo // would make sense, but isn't allowed
        throw RuntimeException("blerg") // throwing is what you do
    }
    assert(a != null) // _We_ know that, but Kotlin doesn't

    println(a + 1) // no smart cast :(
}

As the comments indicate,

  • we can not escape the orElse “branch” by returning from a surrounding scope, and
  • Kotlin is not able to infer that the guarding predicate is true if orElse returns.

The second point bothers me. Even if the compiler can’t infer the assertion (if feeld like it should be able to, in principle, at least for “simple” predicates) on its own, if we could tell it that predicate() ==> trap() called or orElse() terminates => predicate(), it could help it along. Are there some post-condition annotations that can do that?

That we don’t get a smart-cast after the assertion is so weird I’m inclined to call bug.


#5

I guess the null check is just an example, because otherwise you could just use

a ?: run { ... }

That way kotlin also knows about a not being null later.

I’m not entirely sure why that is. Have you tried declaring orElse as inline? It might also have to do something with the declared return type of Nothing (just speculation though).

There is a keep about that here. There is a timeline in there as well. You can start to see the first implementation in Kotlin 1.3.


#6

That does it. Good call, thanks!
(Not 100% why non-line doesn’t work… probably something about which assumptions you can make about where that closure is passed to?)

Keeping the code in a Gist.

Something to look forward to, then!


#7

In Swift guard takes the same expressions as if even though all examples emphasizes unwrapping (converting from nullable to not-nullable in Kotlin terms).

How about

fun <T> T.guard(vararg cond: Boolean, otherwise: T.()->Nothing): T {
    if(cond.all { !it }) {
        return this
    } else {
        otherwise()
    }
}

or for unwrapping:

fun <T> T?.guard(vararg cond: T.()->Boolean, otherwise: T?.()->Nothing): T {
     val value = this ?: otherwise()

    if(cond.all { value.it() }) {
         return value
    } else {
        otherwise()
    }
}

They unlike Swift’s guard allows to check multiple conditions. Also return in case of unacceptable parameters doesn’t make sense. This is why I made the handler to return Nothing, which will force the closure to throw Exception.