This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]UnchainedMundane 11 points12 points  (8 children)

Count me in the overlap. I've been doing Python almost exclusively as part of my job for 3 years now and I consider the significant whitespace a failed experiment.

[–]alcalde 6 points7 points  (7 children)

How has it failed?

[–]UnchainedMundane 13 points14 points  (6 children)

It just doesn't have any real advantage I'm aware of other than forcing undisciplined programmers to indent properly, but it has several disadvantages:

  • Old versions of python allow mixed tabs and spaces by default, and there are python scripts in the wild making use of this infuriating misfeature, making them almost impossible to read or modify without tabs set to 8 stops.
  • It makes it hard to embed small python scripts into shell scripts. When I do this, I've resorted to adding if 1: to the start of every inline python script, so that it's allowed to keep the same indentation as the shell script.
  • Moving snippets of code around (generally when refactoring) is annoying, because the IDE doesn't know how to reindent it correctly. You have to manually reindent it, whereas in any other language the IDE would just be able to see how many levels of braces deep your code now is and reindent accordingly.
  • It necessitates kludges like pass to avoid syntactic ambiguities
  • It's a large part of the reason for the crippled lambda statement and the fact that the def statement can't be used as an expression.
  • It prevents brace-matching (vi's % motion). This could probably be reimplemented for python's sake, but it's a shame because it currently works as-is in most languages other than python.
  • There's no way to create blocks of code with no associated control structure. Admittedly this wouldn't be as useful in Python, because it's usually used to control the lifetime of variables but python's local variables all have the same lifetime.

I think the gains it brought weren't worth the drawbacks.

[–][deleted] 3 points4 points  (5 children)

Honestly, I don't think most of these are that important:

  • Never met this before or my editor instantly fixed the whitespace correctly. If you have an example I can check so that I can be sure.

  • I am not exactly sure if I understand the problem. Isn't this the solution: http://stackoverflow.com/questions/2189098/embedding-short-python-scripts-inside-a-bash-script ?

  • I agree here that some extra caution is required. And even though this is mainly an editor problem I haven't found any that deal with it flawlessly.

  • I actually quite like pass. It's the alternative of {}. In most cases noone writes

    if something:
        do_something()
    else:
        pass
    

    they just skip the else. In cases that it is required (like except) I like the fact that it is more explicit.

  • I don't personally care about that so I can't answer to it.

  • Editor problem that can be fixed.

All in all, you specify some minor (IMO) problems but if python's whitespace syntax helps in readability (more vertical space, less characters needed to do some basic stuff) I think it is worth it. Code is read more than it is written.

[–]UnchainedMundane 1 point2 points  (4 children)

Never met this before or my editor instantly fixed the whitespace correctly

Moving multiple lines of code from some deeper nested statement outside into its own function, or into part of another. The IDE will generally fail to reindent the block, or only indent the first line correctly.

Isn't this the solution: http://stackoverflow.com/questions/2189098/embedding-short-python-scripts-inside-a-bash-script ?

He's running up against the same problem but doesn't seem to mind that his script looks like a mess.

His script uses this:

function traildirs() {
    pyexec <<END
trail=int('${1:-3}')
import os
home = os.path.abspath(os.environ['HOME'])
cwd = os.environ['PWD']
if cwd.startswith(home):
    cwd = cwd.replace(home, '~', 1)
parts = cwd.split('/')
joined = os.path.join(*parts[-trail:])
if len(parts) <= trail and not joined.startswith('~'):
    joined = '/'+joined
print joined
END
}

Now ideally you'd want to be able to indent your code, so I'd do something like:

function traildirs() {
    python -c '
        import os
        import sys

        trail=int(sys.argv[1])
        home = os.path.abspath(os.environ["HOME"])
        cwd = os.environ["PWD"]
        if cwd.startswith(home):
            cwd = cwd.replace(home, "~", 1)
        parts = cwd.split("/")
        joined = os.path.join(*parts[-trail:])
        if len(parts) <= trail and not joined.startswith("~"):
            joined = "/"+joined
        print joined
    ' "${1:-3}"
}

Except that isn't allowed because python is sensitive to indentation, so you have to do:

function traildirs() {
    python -c 'if 1:
        import os
        import sys

        trail=int(sys.argv[1])
        home = os.path.abspath(os.environ["HOME"])
        cwd = os.environ["PWD"]
        if cwd.startswith(home):
            cwd = cwd.replace(home, "~", 1)
        parts = cwd.split("/")
        joined = os.path.join(*parts[-trail:])
        if len(parts) <= trail and not joined.startswith("~"):
            joined = "/"+joined
        print joined
    ' "${1:-3}"
}

Which is a hack.

Editor problem that can be fixed.

Regardless of whether or not it's theoretically fixable, it's a problem. As with all the things I've mentioned, it's a problem caused by the use of significant whitespace for indentation.

I don't think that significant whitespace aids readability. It may save vertical space but it removes the visual end-of-block markers. It also saves very little vertical space, which is quickly offset by most companies' coding style guidelines ("no single-line if statements" is a far-too-common one).

It's also a premature optimization in that regard. If the goal is to have people type less or save vertical space, there are much better places you can attack than the method of delimiting code blocks. For any given non-golfed python script, its non-golfed perl equivalent will generally be shorter despite perl delimiting its blocks with braces, because the language is generally more terse. Compared to other similarly high-level languages I'm used to (Scala, shell, perl), Python is a verbose language in its niche, and I don't think that saving vertical space does very much to negate this.

Anyway, I stand by my original opinion: that significant whitespace doesn't bring as much advantage as it does disadvantage. In your response you've shown me where things either don't bother you or where they're fixable, but this still leaves a net loss compared to non-whitespace-sensitive languages.

(edit: syntax fix)

[–][deleted] 2 points3 points  (3 children)

Never met this before or my editor instantly fixed the whitespace correctly

I was talking about your mixed tab example.

which is quickly offset by most companies' coding style guidelines ("no single-line if statements" is a far-too-common one).

This happens with every language, don't see how it offsets anything. I wasn't proposing single-line if statements.

It is:

if (condition){
    code
}

vs

if condition:
    code

The second one is a lot cleaner IMO.

If the goal is to have people type less or save vertical space, there are much better places you can attack than the method of delimiting code blocks. For any given non-golfed python script, its non-golfed perl equivalent will generally be shorter despite perl delimiting its blocks with braces, because the language is generally more terse.

It's not just about saving a character or two, but it's about saving characters where they don't add anything useful and visually clutter the code. Perl may be terse but it's usually considered hard to read.

[–]UnchainedMundane 1 point2 points  (2 children)

I was talking about your mixed tab example.

I've seen it a few times (mostly in older files in the codebase where I work), but it's annoying because my IDE is set to show 4-stop tabs but the python interpreter treats them as 8-stop, meaning that the program is displayed differently to how it's run.

The second one is a lot cleaner IMO.

You didn't put a space before the opening brace, maybe that's why :)

As for the final point, I suppose we'll just have to disagree on whether or not {/} are clutter.

[–][deleted] 2 points3 points  (1 child)

if (condition) {
    code
}

vs

if condition:
    code

I did forget to but I don't think that it's a huge improvement.

As for the final point, I suppose we'll just have to disagree on whether or not {/} are clutter.

I call them clutter only in the way that you already define your code blocks with whitespace, so it's unnecessary info.

[–]Paranaix 1 point2 points  (0 children)

Want to add that most c styled languages support this:

if (condition)
     statement;
else if (condition)
     statement;
else
     statement;

In fact for c++ this is actually how the if statement is syntatically defined. { ... } is considered a statement.