Implement 'do' to simplify coding

What is ‘do’ about? Well, it’s a type of loop which instead of checking its condition at the beginning, it is checked at the end of the loop. The code is executed first and the condition is checked at the end of each loop. Basically, it avoids using breaks and weird codes with variables, everything is much neater than using while.

Pythonic way

to_do = [
    'Add a task',
    'Delete a task',
    'View all tasks',
    'Exit'
]

tasks_done = []

def main():
        tasks_done.append(to_do.pop(0))
    do len(tasks_done) == 0:
        
main()

or

to_do = [
    'Add a task',
    'Delete a task',
    'View all tasks',
    'Exit'
]

tasks_done = []

def main():
    do len(tasks_done) == 0:
        tasks_done.append(to_do.pop(0))
        
main()

It is a shame that in a high-level language this possibility is not even contemplated.

1 Like

This isn’t a very compelling example, since checking at the top of the loop would be better than unconditionally entering it - if you have no tasks to do, you should skip the loop body altogether, exactly as a while loop does.

(Side note: It is MUCH more common to check the truthiness of the list rather than needing to check its length. Also, I’m not sure whether do is supposed to invert is condition or not, but if it is, you probably want to call it until or something.)

Having a block that ends with its header is going to cause major readability problems. Having something that starts with its header, but only evaluates that condition at the end of the loop, is going to cause minor readability problems. You may want to consider refactoring your code to not require this; for example, using a generator function and a for loop, if it’s really that bad to have a conditional break at the bottom of the loop.

On what basis do you say that this has not been contemplated? If you mean that your PR was rejected, then it’s simply because - as you were told - PRs like that are not the way to make language suggestions.

I didn’t ask you to talk about my code, I appreciate it but I don’t need it. We’re just talking about the ‘do’ here.

If you think the idea of a do-while loop hasn’t been contemplated in the past, you’d be wrong.

Both of your proposed syntaxes have major problems.

In the first syntax, there’s a parsing ambiguity.

if is_weekday():
    tasks_done.append("get coffee")
    task_to_do = to_do.pop(0)
    tasks_done.append(task_to_do)
do len(to_do) == 0:

Where does the if statement end, and the do-while begin?

The second syntax isn’t ambiguous, but the order of evaluation doesn’t match the order in which you read the code. That’s highly undesirable as a language feature, and would be inconsistent with the rest of Python as a language. At the very least, it would lead to “when do I use a do, and when do I use a while” questions, because other than the keyword, the two loops would be identical in appearance, but with radically different behavior.

1 Like
    if is_weekday():
        tasks_done.append("get coffee")
        task_to_do = to_do.pop(0)
        tasks_done.append(task_to_do)
do len(to_do) == 0:

Perhaps the biggest problem is in the ‘else’.

‘do’ is actually an abbreviation for ‘do until’, so the second code I proposed seems more consistent to me.

do len(to_do) == 0:
    if is_weekday():
        tasks_done.append("get coffee")
        task_to_do = to_do.pop(0)
        tasks_done.append(task_to_do)
        break
else:
............

The question is do we need a break on ‘do’, I haven’t thought about it yet.

That’s no less ambiguous.

The intention of my example was the equivalent of:

if is_weekday():
    tasks_done.append("get coffee")

while True:
    task_to_do = to_do.pop(0)
    tasks_done.append(task_to_do)
    if len(to_do) == 0:
        break

No amount of indentation will get you out of that ambiguity with your proposed syntax - you need to have a syntactic marker at the start of the block. And, as PEP 315 highlights, that idea has been previously rejected.

1 Like
if is_weekday():
    tasks_done.append("get coffee")

do len(to_do) == 0:
    tasks_done.append(to_do.pop(0))

It is consistent and results in beautiful, compact code.

The rule is that it always executes the condition even if it is not true at the beginning, it will only check the condition at the end.

Meta note: please take the time to make sure your post says what you want it to say before submitting it. Post-submission edits are fine for fixing typos or formatting markup, etc., but significantly changing your message makes it hard to write a coherent reply.


I’ll admit that I recently ran into a situation where if we had a do-while (or do-until) construct, I would have used it. But we don’t, and I managed to do what I needed to do in a readable manner with a regular while. It also took me 10 years to run into that situation, which indicates to me that it is not a frequent need.

1 Like

But if you used it you would find more solutions, when you see a ‘while 1’ or ‘while True’ remember me.

But you gave an example to support your proposal, so it is natural to debate the merits of this. Language features don’t exist just “because they can”. They exist to make code better. So, what code would be made better by this feature? A proposal looks far better with a compelling example than it does with one that would be better written using existing constructs.

It simplifies it and in the long run when everyone knows its usefulness it will make a prettier code that will require less ‘breaks’ and comparisons with variables that we do not need here because they are discouraged by different programming conventions.

I repeat the same thing, when you see a while 1, while True or you are struggling to get a correct result from a database, remember me.

Why? Because you think you’re the first person to suggest this, or because you’re trying to guilt me into supporting your proposal?

I’ll be honest: it sounds like you’re getting pushback against something that, in your mind, is the greatest idea EVER to have been had, and it bothers you that other people don’t see it the way you do. Well, that’s a fact of life. Not all ideas are accepted by everyone - not even good ideas are. Ideas have to withstand scrutiny. If you get offended by people discussing your idea’s shortcomings, you may do better to build your idea into a modified fork of Python and use it on your own; that’s a legally guaranteed right that you have, and you don’t have to answer to anyone.

At very least, if you want people to take your proposal more seriously, show some GOOD examples where it would benefit the code. Your existing example would become distinctly worse as a do-until, as while to_do: tasks_done.append(to_do.pop(0)) is better in every way.

It’s an example code.

I have been a member of forums for more than 15 years, I already told you that I do not want codes or opinions.

Real life coding:

async def get_ventas(diamantes, operador):
    dict_final = {}
    while 1:
        try:
            async with await connect(**CFG_SQL) as cnx:
                async with await cnx.cursor(dictionary=True) as cur:
                    while 1:
                        dict_final.clear()
                        updated_at = await get_utc_offset()
                        query = SQL_GET_VENTAS % (
                            updated_at,
                            diamantes,
                            operador
                        )
                        async for _ in cur.executemulti(query):
                            result = await cur.fetchall()
                            for row in result:
                                dict_final.update(row)
                        if not ('timestamp_check' in dict_final and dict_final['timestamp_check'] == 1):
                            print_flask(f'[INFO] No hay ventas pendientes, operador: {operador}')
                            await asyncio.sleep(5)
                        else:
                            break
            yield dict_final
        except mysql_error_connection:
            print_flask(f'[ERROR] [GET_VENTAS] Error de conexión a la base de datos')
            await asyncio.sleep(5)
        except Exception:
            trace = traceback.format_exc()
            print_flask(f'[ERROR] [GET_VENTAS] \n{trace}')

vs

async def get_ventas(diamantes, operador):
    dict_final = {}
    while 1:
        try:
            async with await connect(**CFG_SQL) as cnx:
                async with await cnx.cursor(dictionary=True) as cur:
                    do not ('timestamp_check' in dict_final and dict_final['timestamp_check'] == 1):
                        print_flask(f'[INFO] No hay ventas pendientes, operador: {operador}')
                        await asyncio.sleep(5)
                        dict_final.clear()
                        updated_at = await get_utc_offset()
                        query = SQL_GET_VENTAS % (
                            updated_at,
                            diamantes,
                            operador
                        )
                        async for _ in cur.executemulti(query):
                            result = await cur.fetchall()
                            for row in result:
                                dict_final.update(row)
            yield dict_final
        except mysql_error_connection:
            print_flask(f'[ERROR] [GET_VENTAS] Error de conexión a la base de datos')
            await asyncio.sleep(5)
        except Exception:
            trace = traceback.format_exc()
            print_flask(f'[ERROR] [GET_VENTAS] \n{trace}')

As you can see, we have reduced more than 12 lines of code and the code is more readable without so many variables. In addition, we have avoided the use of breaks and made the context much more robust and reliable.

Franco,

A really easy way to get people to not consider your deal is to tell anyone to stop discussing it in a reasonable way.

The reality is that a well-established language that works pretty well really is hard to change without serious evaluation and even consensus.

I have used other languages that have oodles of control structures that may be similar to what you want but that does not mean they are a great idea or often used when available or that the compiler/interpreter has an easy time with it. A few simple commands used well can be easier to deal with.

Here is an example from PASCAL:

repeat
...
until condition 

I think RUBY has a construct like:

begin
  ...
end until condition

I think VBA has a do

Do Until condition
      ...
    Loop

And, of course, there are languages like C with a DO...WHILE:

```c
do {
// body of do-while loop
} while (condition);

There are doubtless many examples of other control constructs.

But as pointed out, workarounds can be fairly trivial and as simple as enclosing the contents of a while loop into an if statement. And, unlike some purists, sometimes a break statement or even using error handling in some way are reasonably pythonic.

There is plenty you can suggest as to why you want the feature and I have shown you are really far from the first to think about it. The people who have built and maintain python are quite aware and many see higher priorities.

People like Chris are not your enemies and perhaps if you step back, you can see this kind of conversation happens with almost any proposal for a change. You need way more than suggesting that any feature in any other language should be added to python.

10 Likes

I echo the OP’s sentiment of missing a do-while construct, rather often having to instead resort myself to the idiom of while True: if condition: break, which works for sure but is simply not as readable as a do-while if it were supported.

I think a dedicated do-while statement without a trailing colon after the while clause can work:

do:
    value = input("Enter a value: ")
while not value

While we’re at it we can also introduce the until clause as a soft keyword:

do:
    value = input("Enter a value: ")
until value

versus the status quo:

while True:
    value = input("Enter a value: ")
    if value:
        break

Some examples of existing CPython code that can benefit from this construct include:

2 Likes

I find the forms you propose very interesting and very understandable.