For as an expression


#1

I know you can do a map which amounts to the same thing, but sometimes a for loop is a little bit more readable. I was surprised to see that a for is not an expression.

val fnxs = for (x in xs) { fn(x) }


#2

Kotlin would need a yield expression to return a (useful) value. yield is on the roadmap for Kotlin 1.1. I’d be interested, whether for will become an expression then.


#3

I wouldn’t be unhappy with the yield at all, but is it really required?

Ie, what’s the difference between

for (x in xs) {
  fn(x)
}

and

val ys = for (x in xs) {
  fn(x)
}

The for without yield can just throw away the value. Depends how fussy you are on if “expressions should never throw away their return value”.

xs.map { fn(y) }
val ys = xs.map { fn(y) }

Is the same kind of thing.


#4

Maybe you could show an example where map is less readable, because in this case I clearly favor map over for.

val fnxs = xs.map(fn)

I think this will introduce unnecessary ambiguity and fragmentation. That’s one of the things that bother me about Scala or Haskell. You end up with 10 different ways to do the same thing.


#5

I agree (with fragmentation). But I don’t think for as an expression would do that, in fact I think its odd its an exception (not an expression). try, if, when are all expressions, why is for is the odd one out.

It’s not a big deal, I won’t try to convince you with toy examples as people will always have their own viewpoint on what is elegant and what is ugly.


#6

Looping over lists, there isn’t much need for a for/yield sort of thing, since that’s what map, or flatmap, or other such things do. A yield statement would be much more useful in a recursive data structure like a tree.

Consider the humble in-order tree traversal. In principle it’s really simple:

class Tree<T> {
    fun traverse(consumer: (T)->Unit) {
        if(isLeaf()) {
            consumer(leafVal)
        } else {
            left.traverse(consumer)
            consumer(nodeVal)
            right.traverse(consumer)
        }
    }
}

But what if you only really want to consume the first N elements of a potentially huge tree, but in sorted order? If you could rewrite that above code to say yield(nodeVal) or something similar instead of just invoking the consumer, now you’ve got a lazy tree traversal without dealing with lazy lists and whatnot.

That would be seriously cool.


#7

What would be a nice way to write this in Kotlin, given that for loops are not expressions.

for ( k <- 1 to rs.getMetaData.getColumnCount ) yield rs.getString(k)


#8

(1..rs.getMetaData.getColumnCount).map { k -> rs.getString(k) }


#9

Thanks


#10

Comprehensions (yield etc) may still be a benefit, but for now one can use coroutine-based generate() from this library: https://github.com/Kotlin/kotlinx.coroutines#generate