For - else clause

Requesting else block with loop statement which won’t iterate.

for (i in emptyList<Int>()) {
    // This won't execute
} else {
    // this will executed
}
2 Likes

forEach modified

fun main(vararg args: String) {
    emptyList<Int>().forElse {
        println("This won't execute")
    } ?: println("this will executed")
}

fun <T> Collection<T>.forElse(block: (T) -> Unit) = if (isEmpty()) null else forEach(block)
4 Likes

I too have wished for a language to adopt such a feature. I seem to remember some language having such a feature, but I don’t remember which.

There may be some confusion however with other languages. Python has a for-else (http://book.pythontips.com/en/latest/for_-_else.html#else-clause) but in that case the else is executed when the loop finishes normally (i.e. no break).

1 Like

Following the pattern of first() and firstOrNull() methods, might be better to call it forEachOrNull

2 Likes

Also can do

inline fun <T> Collection<T>.forEachOrNull(block: (T) -> Unit) = takeUnless(isEmpty()).forEach(block)
1 Like

Or you can do something like this:

infix inline fun <T> List<T>.orElse(predicate: () -> Unit){
    if (this.count() == 0) predicate()
}

infix inline fun <T> List<T>.forElse(predicate: (T) -> Unit) : List<T> {
    this.forEach(predicate)
   	return this
}

fun main(args: Array<String>) {
    (0 until 0)
    .toList() forElse {
        println(it)
    } orElse {
        println("It's empty")
    }
}
1 Like

Using only standard language & stdlib functions it looks Ok and quite readable IMHO:

myList.apply { 
    forEach { 
         println("This executes on each item $it")
    }
    if (isEmpty()) { 
        println("This executes when list is empty")
    } 
}
5 Likes

The issue about this feature is https://youtrack.jetbrains.com/issue/KT-476.
You can vote for it, if you find it useful. Also, if you could post a concrete example, how would you use that feature, it would make your vote more substantial.

2 Likes

Well how beautiful is Kotlin ?! It allows us to do what we want with what it has, and it has exactly what we want

3 Likes

In this case, we’re lucky that Kotlin can do it with what it has. In many other cases, you can’t. A macro facility would be very nice to have.

(I’m not talking about C-style macros here, but rather something like Lisp or Elixir).

1 Like

I just want to revive this conversation.

Yes, you don’t need this, but then again you don’t need Kotlin, if you want to go down that path.

Kotlin is, or at least a big part of it is, all about being beautiful and elegant, and adding an else clause to a for-each loop is only the natural thing to do.

As you said yourself, it “looks Ok”.

But why settle for Ok for you can make it look great? :slight_smile:

Because it always cost something…

If you want to add a construct, the compiler becomes more expensive and there has to be more constructs to be thought. And not an unimportant thing, the man hours put in this feature, can’t be given to something else.

On the other hand, if you create it yourself, it might not look exectly how you want it and it is not a standard, but the compiler and learning curve remains the same.

1 Like

It’s even worse than that…

I’ve used kitchen-sink languages, where every pet feature got thrown in. And I’ve hated them.

For one thing, each extra feature makes the language that bit longer and harder to learn. But that’s a relatively minor expense.

And cool features can be quite handy when you’re writing fresh code.

But when you come to debug your code, to work out what on earth the compiler’s complaining about or why it does something unexpected when run, each extra feature can multiply the amount of head-scratching and cursing. Since people spend something like ten times as long maintaining code as writing it, that can be pretty significant. And you’ll still have to be able to read and follow your code when you come back to it next week, next month, next year.

But even worse is when you come to read someone else’s code… The more options the language gives you, the more different ways other people can use it. And the more combinations you need to be able to understand. Even if you’re in a team of one, you’ll still need to read other people’s code when you debug into libraries, read tutorials, or visit sites such as this one.

Kotlin’s lack of complexity is one of the things I really like about it! By hiding some of the unnecessary complexity of Java (primitives, arrays, getters/setters, &c), they made room for new features; and by choosing those very wisely, they made a language which is hugely powerful and expressive without being very big or complex. It’s relatively easy to learn, to use, to debug, and to read.

So each new feature must earn the incremental complexity it adds. It must justify itself by doing something that would otherwise be impossible, or at least too awkward.

And in this case, I can’t see that it does. It implements a pattern I’ve very rarely needed in any of the languages I’ve used. And the language already has several reasonable ways to implement that pattern – none quite as concise, but for such a rarely-needed pattern that’s fine. Maybe my experience has been highly unusual. But I’m afraid the relative lack of interest in this proposal suggests otherwise.

3 Likes

The problem is that Kotlin gives you enough flexibility to make code confusing at times, but not enough power to make it flexible enough. For example, a solution to the original request looks like this:

class XFor<T>(val list: Iterable<T>, val fn: (T) -> Unit)

fun <T> xfor(list: Iterable<T>, fn: (T) -> Unit): XFor<T> {
    return XFor(list, fn)
}

infix fun <T> XFor<T>.xelse(elseFunction: () -> Unit) {
    val iterator = list.iterator()
    if(iterator.hasNext()) {
        iterator.forEach(fn)
    } else {
        elseFunction()
    }
}

fun foo() {
    val x = listOf(10, 11, 12)
    xfor (x) {
        println(it)
    } xelse {
        println("No result")
    }
}

This seems to work really well, until the developer decides that the else part is not needed anymore and removes it. All of a sudden this code will do nothing, because the logic to run the loop is in the else part.

If one wants to argue that Kotlin’s limitations are there to prevent confusing code, then the features that I used in the above example should be removed as well. If not, there should be more flexibility to allow for the implementation of this feature in a more complete way.

I’m coming from the Lisp world where these kinds of tricks are perfectly normal, so my perspective is a bit different compared to most people.

Not a great example since it’s easily remedied by a different implementation:

class XFor(val isEmpty: Boolean)

fun <T> xfor(list: Iterable<T>, fn: (T) -> Unit): XFor<T> {
    val iterator = list.iterator()
    val isEmpty = !iterator.hasNext()
    if(!isEmpty) {
        iterator.forEach(fn)
    }
    return XFor(isEmpty, fn)
}

infix fun XFor.xelse(elseFunction: () -> Unit) {
    if(isEmpty) {
        elseFunction()
    }
}

fun foo() {
    val x = listOf(10, 11, 12)
    xfor (x) {
        println(it)
    } xelse {
        println("No result")
    }
}

Note that Kotlin’s language/library designers value readability, not conciseness. Hence the extremely clear if not quite as concise:

1 Like

@elizarov, I don’t think this helps in any way to readability. On the contrary, make a basic programming block really hard to read (and write) compared to the proposed

for (i in myList) {
  ...
} 
else {
  ...
}

For the reference this else clause exists in Python (edited ah oops, in fact it’s not the same behavior) and in Velocity 2.1+.

The proposed for/else construct in pseudo code reads as follows:

For item in items:
Do something
If the loop was empty:
Do something else

Notice how the “else” really means “if the loop is empty”. IMO the clearest way to say “if the loop is empty” is an if-statement. Every time a reader came to a for/each, they would have to understand it in their head to mean an if-statement.

The for/else construct is made even more confusing as Python already uses it to mean “if the loop iteration completed without error”. To be fair, python’s interpretation is just as natural as the proposed one here.

The benefit to adding for/else is small. The biggest benefit is “better readability” but it’s arguably harder to read than an explicitly written if (it.isEmpty()) { /* ... */ }. Even if it was unquestionably more readable with for/else, it doesn’t have a strong impact to warrant much priority or added maintenance.

Maybe there’s an alternative languages change that would be more impactful? For example, having loops be expressions instead of statements? That proposal would enable OPs exact request on a language level and potential support a lot more. (I’m not advocating for that, just wanted to point out that there are other ways to get this feature)

4 Likes