V: For loop ambiguity; can't run for loop example in playground

Created on 13 Jan 2020  路  10Comments  路  Source: vlang/v

V version: Latest playground
OS: Android

What did you do?
Clicked run on the while version of the for loop example from the documentation.

mut sum := 0
mut i := 0
for i <= 100 {
    sum += i
    i++
}
println(sum) // "5050"

What did you expect to see?

5050

What did you see instead?

unexpected token: `+=`
    2| mut i := 0
Bug Playground Compiler

All 10 comments

What I was attempting to test was the ambiguity of the for loop when used with the in operator.

mut nums := [1, 2, 3]
mut num := 1
for num in nums {
    // ...
}

Is this going to loop over nums, assigning each value to num successively? Or will the in expression be evaluated as true and the for loop will operate as a while loop?

Making up an example here to show where this could be confusing. A user has selected some option represented by an int. We have an array of invalid choices, and we want the user to continue to select an option until they pick a valid one.

opt int := get_user_opt()

invalid_opts := [3, 4]
for opt in invalid_opts {
    // ?
}

This is much clearer:

opt int := get_user_opt()

invalid_opts := [3, 4]
while opt in invalid_opts {
    // ask the user to choose again until valid
}

I suppose the clearest way in V is to nest an if inside an infinite for?

opt int := get_user_opt()

invalid_opts := [3, 4]
for {
    if !(opt in invalid_opts) {
        break
    }

    // ask the user to choose again until valid
}

What is the best (most readable) way to do this?

Clicked run on thewhile version of the for loop example from the documentation.

mut sum := 0
mut i := 0
for i <= 100 {
    sum += i
    i++
}
println(sum) // "5050"

Just put the code inside main function:

fn main() {
    mut sum := 0
    mut i := 0
    for i <= 100 {
        sum += i
        i++
    }
    println(sum) // "5050"
}

@dhonx These examples used to (and should) work directly from the documentation. I believe V runs these as "V scripts", wrapping the content in a main function.

@jtkirkpatrick Yeah, I agree with that.

I understand why the parser would do this, but I'm not sure about the readability.

This throws an error saying opt is redefined, it wants to iterate over invalid_opts:

fn main() {
    invalid_opts := [2, 4]

    mut opt := 2  // this would come from some get_user_opt() function

    for opt in invalid_opts {
        // keep asking for opt until valid
    }
}

This will be run as a traditional while loop:

fn main() {
    invalid_opts := [2, 4]

    mut opt := 2  // this would come from some get_user_opt() function

    for (opt in invalid_opts) {
        // keep asking for opt until valid
    }
}

It appears that the for loop as the only looping construct was borrowed from Go, but Go doesn't support the for x in y syntax (from what I can tell on their docs, I don't use Go).

To utilize the in expression as a boolean condition for a loop, V requires parenthesis which is weird, and still doesn't make the loop readable. for (opt in invalid_opts) still reads like for opt in invalid_opts.

The for x in y syntax is concise, clear, and definitely should be supported. But with in also being used for membership testing, ambiguity is introduced to the for loop. I understand the idea of there being one way to do something, but maybe there shouldn't be just one keyword to do everything. I think the while loop would help here. It's behavior is familiar and clear.

As documented, "The for value in values loop is used for going through elements of an array", and when you use it like for (value in values) it parses value in values first, which is recognized as a boolean in operator, so it works as expected via reading the documentation.
As for readability, I think that a for each value in values would be easier to read, and leave the legacy for as boolean only, but I can't make any decision on that respect

I suppose this is available, though still not abundantly clear:

fn main() {
    invalid_opts := [2, 4]

    mut opt := 2  // this would come from some get_user_opt() function

    for ;opt in invalid_opts; {
        // keep asking for opt until valid
    }
}

@PibePlayer I'm ok with for x in y, it's fairly common and readable. It would be confusing to me if for each x in y meant iterate and for x in y meant loop until x is not in y.

I think the clearest thing is to add while. It's well known and should be immediately clear to programmers regardless of language background. Or else write the while form of for loops that use in as above, or with parenthesis.

I think the clearest thing is to add while. It's well known and should be immediately clear to programmers regardless of language background. Or else write the while form of for loops that use in as above, or with parenthesis.

I'd second this proposal.

Another thought: the syntax for ; x in [ 1 2 3 ]; {} seems way clearer than for (x in [ 1 2 3 ]){}. What about disallowing the second syntax (for (<variable> in <iterable_expr>)) to avoid confusion and potential issues?

There's no more confusion.

x := ...
...
for x in [1,2,3] {
}

This no longer compiles because of redefinition of x.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

radare picture radare  路  3Comments

oleg-kachan picture oleg-kachan  路  3Comments

choleraehyq picture choleraehyq  路  3Comments

markgraydev picture markgraydev  路  3Comments

cjmxp picture cjmxp  路  3Comments