hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler
Other
5.24k stars 224 forks source link

[QUESTION] cpp2 for loops #1063

Closed dutkalex closed 1 month ago

dutkalex commented 1 month ago

Hi! My question is rather simple, but I could not find the answer in the documentation nor in other issues. What is the cpp2 equivalent for:

for ( int i = a; i < b; ++i ){
  // loop body
}

All the examples in the documentation seem to be range-based for loops, and the closest I could find (which is obviously not the right answer) is:

for std::views::iota{ a, b } do ( i ){
  // loop body
}

Thanks for the amazing work! Best regards, Alex

hsutter commented 1 month ago

That's a good question I should cover somewhere. Here's the quick answer:

auto cpp1() {
    //  Prints: 0123456789
    for ( int i = 0; i < 10; ++i ){
        std::cout << i;
    }
    std::cout << "\n";
}

main: () = {
    cpp2();

    //  Prints: 0123456789
    (copy i := 0) while i < 10 next i++ {
        std::cout << i;
    }
    std::cout << "\n";

    // Same, just my personal whitespace style
    (copy i := 0)
    while i < 10
    next  i++ {
        std::cout << i;
    }
    std::cout << "\n";
}

Line by line (and why I like being able to write the parts on individual lines):

I should add an example to the docs... I'll keep this open until I do that. Thanks!

hsutter commented 1 month ago

Now added here: https://hsutter.github.io/cppfront/cpp2/functions/#for-while-do-loops

Thanks!

dutkalex commented 1 month ago

@hsutter You closed this thread but it turns out that your answer left me with more questions! 😅

  1. I understand that one of the goals of cpp2 is simplification of the language through generalization and an overall reduction of the number of concepts. But in this case, having to revert to a while loop is a net loss in expressivity of the language. I we were to follow this principle dogmatically, all for loops would have to be eliminated because every for loop is just a special case of the more general and powerful while loop concept. However, the while loop is much-more error-prone, and I am sure you will agree that we don't want this kind of concept count reduction because having for loops in our toolbox enables us to better express our intent as programmers. For scientific computing purposes (I take this example because this is where I come from as a C++ programmer), not being able to express in a simple way index-based iteration patterns would be a deal-breaker. However, I do not advocate for the error-prone C-syntax either, but IMO having a concise syntax such as for idx_range(a,b) do (i){ ... } available is important to make cpp2 code safer. What do you think of it?
  2. I understand how the for loop syntax is built as for <range> do <lambda_expr>, which is actually very familiar to me as a HPC fellow, because we are used to writing thinks like hpc_framework::parallel_for( exec_policy, range, [&]( int idx ){ ... } ); in cpp1 syntax. However, the absence of the prefix : from the cpp2 lambda syntax in the cpp2 for loop syntax, combined with the fact that the cpp2 for loop syntax does not read left to right is a sign that this generalization is not very natural. I would also add on a personal note that I really don't like the do token, because it does not convey much meaning as a keyword, and is confusing because it connotes a do ... while loop. IMHO it is like having a then keyword after an if statement: it makes the syntax more verbose and does not provide any additional meaning. On top of that, having a do token justifies diverging the syntax from the if and while syntax which both require the body to be enclosed by curly braces. I would argue that having a coherent syntax among these 3 control flow structures should be a higher-priority goal than seeking unification with the function syntax. With this in mind, my question is therefore: why not opt for a more intuitive syntax for the cpp2 for loop? For example, Python's for i in range(N) is very natural and reads like english (they did a really great job on this one IMHO), and something like <optional label :> for <optional in(default), out, inout or move> <elm> : <range> { /* body with mandatory {} */ } would be simpler, more familiar, easier to read and more coherent with the rest of the cpp2 syntax? What do you think of it?

Anyway, thanks for the quick answer and the amazing work overall Best regards, Alex

dutkalex commented 1 month ago

Other related threads for reference: #432 #834 #386

dutkalex commented 1 month ago

Moving this to discussions (see #1073), as it seems more appropriate