Live data from Hacker News

Avoid Else, Return Early (2013)

blog.timoxley.com

581–590 of 601 posts

Re: Avoid Else, Return Early (2013)

#581

Earlier quoted context omitted.

Most bracket languages aren't strictly context-free either (the Dangling else problem [1] alone somewhat guarantees that most programming languages are not strict, unambiguous CFGs in a mathematic sense). There's no right/wrong/superiority in context-free versus context-sensitive, they are tools in the toolbox and not really "levels in a superiority hierarchy". [1] https://en.wikipedia.org/wiki/Dangling_else

> There's no right/wrong/superiority in context-free versus context-sensitive Dismissing the whole field of formal language theory with that statement is so bogus, it "is not even wrong." There are tools and techniques for parsing context-free languages that are impossible with context-sensitive languages. Parser generators, structured editing, metacompilers, composable grammars; those are all impossible with context…

I'm not dismissing anything. I'm pointing out the boundaries are fuzzier than hard cut-offs and it's weird to assign moral superiority to one side of the grass or the other. Most parser generators/composable grammar tools, etc, have tools for dealing with context sensitive sections of a language, just as they use regular expressions for tokenizing and allow regular language sub-languages.

I can point to ambiguous context-free grammars and say whitespace sensitivity is ok, because the tools to fix the one are often the same to fix the other. (Ambiguous context-free grammars do turn out to make context sensitive languages, because it's a blurry line in the grass between them.)

Whitespace sensitivity is one of the easier context sensitivity challenges to embed as a sub-language in an otherwise (mostly) context-free language, because you can represent it entirely as pseudo tokens from a simply modified lexing phase in a traditional CFG parser. Python is an easy and clear proof, as that is exactly what it did (Python is also not purely a CFG after whitespace tokenization because of also how it handles the dangling else, but we've already mentioned those weeds).

Even if you don't find the boundaries between the classes of languages fuzzy (and Parser Combinators and PEG grammars have a lot to say here), context-sensitive languages are a tool in a growing toolbelt, not a "complex" evil to be demonized.

Re: Avoid Else, Return Early (2013)

#582
post #579

Earlier quoted context omitted.

Mixing finally and RAII into the same category doesn't really make sense here. And I'm not saying that finally depends on checked exceptions, I'm saying that pattern is very brittle. You change lower code to throw a new exception, and you change the above code to catch it like you're supposed to, but now the middle code has no idea that there's this new exception and leaks resources. So you end up either having britt…

> middle code has no idea that there's this new exception and leaks resources You misunderstand how `finally` works. The whole point is that it runs regardless of exception type. So the resulting code in the middle layer doesn’t need to know anything about the code it calls, and doesn’t leak resources, even when calling or called code changes: It’s not brittle. > or you're wrapping pretty much all function bodies wit…

I just had a brain fart.

Re: Avoid Else, Return Early (2013)

#583
post #549

Earlier quoted context omitted.

> I am arguing this is worse, since it expresses the logic in a more convoluted way. Ok, I really wonder if in this special case the logic just looks _odd_ because of bracing styles. (bear with me) This is very easy to understand, where as the parent example, not as much. fun max(a, b) { if a > b { return a } return b }

Regardless of bracing style, I still interpret `return a` as a special case with respect to control flow due to the indentation. `return b`, on the other hand, is on the "happy path" — if this wasn't simply a max function, I'd assume from the indentation that it'd return `b` most of the time. A max function is one of those rare moments where a ternary statement just seems right, but it's an admittedly simple example.

>I still interpret `return a` as a special case...

I think this is interesting. I wonder if our personal experiences with how we learned programming, maybe first languages or first teachers or jobs, etc... would effect our perceptions on logic flow.

I simple don't see anything unusual about the logic flow in the example. It's a simple if/else - return with less cruft to me.

Re: Avoid Else, Return Early (2013)

#584

Earlier quoted context omitted.

> The less scrolling, tab management, and other context switching I have to do to look at those "2" functions, the easier it is on my memory. I don't disagree, but the sentiment seems to be "fit everything on the screen at all times", which seems like one of those rules that may take more time to implement than it saves in actual time. I work on projects with upwards of 100k lines of code consistently, the very idea…

I tend to work on gamedev codebases roughly near a 1mloc (both above and bellow.) In one recent refactoring I recently managed to get a class declaration (that was previously local to a single .cpp and had all functions defined inline in one go) to fit on a screen. I use small fonts, 4k resolution monitors, portrait orientation, and I still had to cull some "GetFoo gets a foo" style worthless doc comments to accompli…

It sounds like we are on the same page. It's hard to tell with brief forum comments what people mean. I am just used to the reality that you _must_ switch contexts all the time, and it's _normal_ simply because the code bases are too big.

Yes, it's nice when things are close together, but in my case it's so rare as to not be point to consider trying to achieve, but more like a mental joke like "Hey, Bob. Come here and check this out! Two functions and template I am working on all fit on the same screen!" And Bob's like "wooooah, dude". And then we take a group selfie with my monitor and have a laugh, print out a poster of the selfie for the bulletin board for a few months, then throw it away during a cleanup and move on...

But to have people say "It's important to save whitespace so stuff can all be on the screen to avoid context switching" seems completely and utterly pointless.

Re: Avoid Else, Return Early (2013)

#585
post #266
post #127

Earlier quoted context omitted.

I disagree. Consider the absurd example where every single statement is crammed into a single line. Not exactly readable to me? Sometimes more verbose code is more readable, and sometimes it is the presentation, and sometimes this is "superfluous lines", that increase LOC.

That's covered by "all things equal". The argument against placing braces on their own line is that this: if (a) { print(a) } conveys exactly as much information as this: if (a) { print(a) } while taking up more space. The thing the brace tells you is already told by the indentation, so the brace is on a superfluous line.

Maybe it's just me, but I find the first version easier to parse visually. When the coding convention forces me to put the brace on the same line, I end up doing:

    if (user.exists()) {

        destroyHuman(user);
        hideRemains(user);
    }

Re: Avoid Else, Return Early (2013)

#586
post #549

Earlier quoted context omitted.

Regardless of bracing style, I still interpret `return a` as a special case with respect to control flow due to the indentation. `return b`, on the other hand, is on the "happy path" — if this wasn't simply a max function, I'd assume from the indentation that it'd return `b` most of the time. A max function is one of those rare moments where a ternary statement just seems right, but it's an admittedly simple example.

> I still interpret `return a` as a special case... I think this is interesting. I wonder if our personal experiences with how we learned programming, maybe first languages or first teachers or jobs, etc... would effect our perceptions on logic flow. I simple don't see anything unusual about the logic flow in the example. It's a simple if/else - return with less cruft to me.

Possible, and also shaped with recent language experiences. My first languages were JavaScript and C++ pretty much simultaneously. In both I'd write a max function with a ternary operator. It signals to me — in the same way as if/else — that no outcome is expected to be more usual or typical than the other, and that no path is more notable or interesting that the other.

The control flow isn't confusing either way, and some language linters will insist against if/else in this example, but I see the if/else approach with slightly more clarity. In general, though, I'm a big proponent of early return.

Re: Avoid Else, Return Early (2013)

#587

Earlier quoted context omitted.

> There's no right/wrong/superiority in context-free versus context-sensitive Dismissing the whole field of formal language theory with that statement is so bogus, it "is not even wrong." There are tools and techniques for parsing context-free languages that are impossible with context-sensitive languages. Parser generators, structured editing, metacompilers, composable grammars; those are all impossible with context…

I'm not dismissing anything. I'm pointing out the boundaries are fuzzier than hard cut-offs and it's weird to assign moral superiority to one side of the grass or the other. Most parser generators/composable grammar tools, etc, have tools for dealing with context sensitive sections of a language, just as they use regular expressions for tokenizing and allow regular language sub-languages. I can point to ambiguous con…

> it's weird to assign moral superiority to one side of the grass or the other

Algorithmic complexity is not "moral superiority." The whole point of formal language theory is to show how the different languages differ in terms of how difficult they are to work with.

> just as they use regular expressions for tokenizing and allow regular language sub-languages

That is because regular languages are a subset of context-free languages. There is nothing special to "allow" there.

> I can point to ambiguous context-free grammars and say whitespace sensitivity is ok, because the tools to fix the one are often the same to fix the other.

No they are not. Precedence rules and extra lookahead will help with ambiguous context-free grammars but not with context-sensitive ones. There is also a whole class of algorithms, such as GLR, specialized to handle ambiguous CFGs efficiently.

> Whitespace sensitivity is one of the easier context sensitivity challenges to embed as a sub-language in an otherwise (mostly) context-free language, because you can represent it entirely as pseudo tokens from a simply modified lexing phase in a traditional CFG parser.

That statement is nonsense. There is no way to "embed" a context-sensitive language in a CFG. What you are saying is that it is "easy" to make a whole context-sensitive parser just to produce a context-free language so you can pass that to another CFG parser. That is not "easy," that is bolting crap onto other crap.

Re: Avoid Else, Return Early (2013)

#588

Earlier quoted context omitted.

I'm not dismissing anything. I'm pointing out the boundaries are fuzzier than hard cut-offs and it's weird to assign moral superiority to one side of the grass or the other. Most parser generators/composable grammar tools, etc, have tools for dealing with context sensitive sections of a language, just as they use regular expressions for tokenizing and allow regular language sub-languages. I can point to ambiguous con…

> it's weird to assign moral superiority to one side of the grass or the other Algorithmic complexity is not "moral superiority." The whole point of formal language theory is to show how the different languages differ in terms of how difficult they are to work with. > just as they use regular expressions for tokenizing and allow regular language sub-languages That is because regular languages are a subset of context-…

> Algorithmic complexity is not "moral superiority." The whole point of formal language theory is to show how the different languages differ in terms of how difficult they are to work with.

Chomsky's hierarchy was designed for production from categories of languages. That it doubles as a useful rule of thumb for algorithmic complexity in parsing is a fascinating dualism in mathematics. That those same rules of thumb reflect basic automaton abstractions is even more fascinating. This is also where it seems the clearest analogy lies to why I find your arguments to "complexity" so useless. It sounds to me like a strange steampunk form of the "640k is all anyone will ever need" fallacy: "why use Turing machines when Pushdown automatons will do just fine?"

Yes, there are a lot of great tools for working with CFGs, just as we've pushed Regular Expressions far past the boundaries of formal Regular Languages, we push these same tools past the boundaries of formal CFGs. We aren't actually constrained by the limits of only using Deterministic Finite Automata or Pushdown Automata in our programming, we have vast Church-Turing–level state machines at our power completely and easily capable of tracking contexts/states.

The "context" in a whitespace-sensitive language is "how many spaces have you seen recently?". This is not a hard question, this is not some mystical "context sensitivity" that needs an entire "context-sensitive language parser". It's a handful of counters. That is the very definition of easy, that's the built-in basics of your average Church-Turing machine, keep a number on the tape and update it as necessary.

Python's specification for the language defines the language in a CFG BNF just as the majority of brackets languages. The one concession to its whitespace sensitivity is that it expects from its lexer INDENT and DEDENT tokens. Those tokens are added simply by counting whitespace between lines, seeing if there is a relationship. After those tokens are in the stream (along with the rest of the tokens defined in their associated (Mostly) Regular Languages) it is parsed by whatever CFG automaton/algorithm the Python team wants (LR, GLR, LALR, PEG, etc). That's not "bolting crap onto other crap" by most stretches of the imagination, that's using a pretty simple stack of tools that any programmer should be able to (re-)build, and not one at all more complicated than (and in fact built entirely inside) the classic tokenizer/lexer feeding a parser stack.

Re: Avoid Else, Return Early (2013)

#589
post #556

Earlier quoted context omitted.

> If it was this simple we wouldn't be still arguing about coding styles decades after they were invented. I disagree with the logic in this sentence. New coding styles are invented all the time. The guard statements in the article were only formalized in the late 90s, for example. These arguments about coding styles "decades after they were invented" are the only way we know which work and which don't. It's not a ma…

Late 90s were 2 decades ago :)

Guards were brand new in the 90s. Unless you're going to argue programming started in the 90s, your point is irrelevant.

Re: Avoid Else, Return Early (2013)

#590

Earlier quoted context omitted.

> it's weird to assign moral superiority to one side of the grass or the other Algorithmic complexity is not "moral superiority." The whole point of formal language theory is to show how the different languages differ in terms of how difficult they are to work with. > just as they use regular expressions for tokenizing and allow regular language sub-languages That is because regular languages are a subset of context-…

> Algorithmic complexity is not "moral superiority." The whole point of formal language theory is to show how the different languages differ in terms of how difficult they are to work with. Chomsky's hierarchy was designed for production from categories of languages. That it doubles as a useful rule of thumb for algorithmic complexity in parsing is a fascinating dualism in mathematics. That those same rules of thumb…

> This is also where it seems the clearest analogy lies to why I find your arguments to "complexity" so useless.

Honestly, from everything you have posted so far, I really get the impression that you have never worked on programming language parsing, or with large code bases. The difference in algorithmic complexity is not "useless," it is the difference between being able to compile millions of lines of code in a few seconds on modest hardware, and needing a cluster just to get your builds done:

https://community.embarcadero.com/blogs/entry/compiling-a-mi...

Post reply on HN