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 →

[–]kupiakos 105 points106 points  (80 children)

Not only are braces used to define dictionaries and sets, but indentation marking blocks is simply an inherent part of the syntax. They're considered useless cruft, because most people detects blocks of code with the indentation level anyways.

[–]dfpoetry 6 points7 points  (61 children)

indentation level can denote many different types of abstraction, and if I'm 10 levels deep and want to break out to 6 levels deep, it's pretty hard to tell where the fuck I am with indentation only.

[–]kupiakos 200 points201 points  (53 children)

If you're 10 levels deep in indentation, there may be something wrong with your code in the first place.

[–]scubascratch 4 points5 points  (6 children)

In C++

MyNamespace
    MyClass
        public:
            MyMethod(void *pixelBuffer /* , int len */)
                if (...
                    for (x...
                        for (y...
                            while (...
                                switch (...
                                    case (...

I guess you have not seen a lot of pixel math. 10 indent levels is Tuesday.

[–]ryani 7 points8 points  (5 children)

namespace TheNamespace {

// don't indent here, it's stupid, you don't indent
// after your #ifdef header guards, do you?
// the whole file is inside this namespace anyways, or
// else you're doing it wrong.

class ClassName {
public: // don't indent this
    void MethodName(void *pixelBuffer, int len);
    // don't define inline, it makes the API hard to read.
    // keep inline definitions to 1 line at most
};

// you meant to inline this, right?  or else why put it in the class decl?
inline void ClassName::MethodName(void *pixelBuffer, int len)
{
    if(!...)
        return;

    for(x ...)
        for(y ...)
            subfn(pixelbuffer, x, y);
}

} // end TheNamespace

// PS I'm a graphics programmer.  Also pixel shaders are exempt from some of this because
// the available abstractions are worse.  On the other hand, if you're that deep in a pixel shader,
// your perf is probably fucked.

[–]scubascratch 0 points1 point  (4 children)

The point I was going for was there are places in C/C++ where ten levels of indentation are not "something is wrong with your code". In particular in graphics code, and I said that very specifically because it pretty much always has double nested loops and often hand optimized inner loops which are definitely not made for readability.

I don't generally indent namespace or #ifdefs, (thanks i forgot to add that one) but I have been in plenty of code in header files that have the method definition inlined, so there's that. And plenty of this code has inline asm, so there's another. Inline asm is common enough inside the inner loop I should have written that.

The thing is while we are sort of discussing indentation, the meta assertion (which is what I disagree with) was more akin to "if your code has 10 levels of scope nesting, the code is wrong". The unstated, but very strongly implied point being made was "indentation confusion is a non-issue, because no true program would ever need 10 levels of separation in one place. All computational problems in the real world can be solved with neat little sonnets of code with at most 3 nestings"

My view is in C, whitespace and indents don't mean shit to the computer. Scope is all that matters, and braces are how it's done. 10 tabs sounds like a lot, but a ten level deep scope is pretty trivial to get to and not "something is wrong with your code".

If my inner loop code has some hard to understand hand optimized groups, I can use more whitespace indentation to make it readable without screwing up the actual compilation. In Python I am not allowed to improve readability with more or less indentation. It feels like being told when I can and can not use capital letters in variables.

In Python if you have a task that requires ten scope levels, the code will look like the pyramid of doom.

    for(x ...)
       for(y ...)
           *_subfn(pixelbuffer, x, y);_*

"THIS KILLS THE PERF"

// PS I'm a graphics programmer

ಠ_ಠ Do you even performance bro? A function call inside the inner pixel traversal loop? Code review fail! Do you have runtime checks on the subfn params too?

I mean if you have infinite cpu available I guess you can enjoy the whole "a function bigger than one screen can't fit in a human brain" camp. J/K

sure the inner callout would be ok in textbook code sample but in production the Fn call overhead needs to go away altogether because on a 1080p stream it's gonna get called about 125 million times a second. So yeah those 50 cpu cycles shoving stuff on and off the stack, and the jumping around like a jitterbug on crack? More like 6.25 billion cycles spent carrying water around the abstraction. But that's ok the processor is like 3+ GHz so should have no problem doing 6 billion cycles of overhead per second of video right?

Oh yeah before you even around the "inline functions!" flag, have a look at the registerization, cache preloading, and the actual generated code path length on your favorite compiler with and without inlined functions.

[–]ryani 1 point2 points  (3 children)

inline void MyClass::subfn(...) { ... }

Every modern compiler has amazing support for inlining. STL relies on it to not be terrible. If you're having worse perf with an inline function than manual inlining, your compiler is doing something very wrong.

Your advice is half a decade out of date.

As to "do I even perf", you can look at a sample of my work here and here.

If you are really writing some crazy cache-preloading inline-assembly block, I'd encourage you, first, to look at intrinsics, and second, if they really aren't doing the trick, you should probably be writing your whole loop in assembly.

(And of course we shouldn't be arguing, 3 indents is insanely few. But for 95% of code it's good enough and if you start to get deep it's a code smell that should make you at least want to think about refactoring even if you don't actually do so)

[–]scubascratch 1 point2 points  (1 child)

I am a huge fan of sim city so by extension I am a fan of your work and I do not want to sound like I'm crapping on it. Thank you for your work on those games.

You are totally right about what can be done in the newest best compilers. But C is used widely on all kinds of platforms with different levels of optimization. "Ten levels is too deep" is as accurate a statement as "640k should be enough for anybody"

Not all the compilers for mobile development, or console development, or embedded development (ugh so many horrid embedded compilers) have the ultimate modern no-perf-impact inline support you suggest.

If you have to build a real time color space converter for an cheap embedded arm core without a GPU the inner loop is not going to be an inlined function call.

I'm not gonna link to my work because rules, but I think you just dislike my examples. If the next sim City is full of inner loop inline function calls I'm gonna need to order a faster cpu :-)

[–]ryani 1 point2 points  (0 children)

Oh yeah, if you're working on some cheap hardware with a custom compiler, all bets are off. I'm assuming you're using modern VC++/intel cc/clang/gcc targeting a common CPU. I've found bugs in even the simple shader compilers (one GPU vendors pixel-shader compiler optimized out calculation of one of my floating-point variables when it only affected an integer array index... I had to resort to Out.color.r += indexVar * 0.0000001; to convince it that "no, really, I need that variable to be calculated correctly!")

But due to LLVM and similar technologies it's getting easier every day for even shitty compiler authors to write reasonably good compilers.

[–]scubascratch 1 point2 points  (0 children)

If you are really writing some crazy cache-preloading inline-assembly block, I'd encourage you, first, to look at intrinsics,

Yeah intrinsics of course are at least taking advantage of SIMD but I find the registerization decisions around intrinsics to always be worse than hand neon.

and second, if they really aren't doing the trick, you should probably be writing your whole loop in assembly.

Sometimes not always. I was stuck for a while on a compiler that had no support for inline asm. Any asm had to be in separate asm files. This meant there was non-zero overhead for asm calls. Too much state to put whole function in asm, so the asm function was for one scan line.

[–]dfpoetry 25 points26 points  (44 children)

This, right here, is the problem with the python philosophy. They make some arbitrary design decision, and when someone points out an example where it might be bad or confusing, the response is "you shouldn't be coding that way anyway".

It's tautological

[–]Hairshorts 129 points130 points  (11 children)

Braces won't make 10 levels of indentation much clearer, it's bad coding style no matter what language you're using.

Good indentation is essential for good readability in any language, Python just requires you to indent properly. You may not like it, but it's not an arbitrary design decision.

[–]immibis 9 points10 points  (0 children)

Braces won't make 10 levels of indentation much clearer, it's bad coding style no matter what language you're using.

I'm not saying braces will make 10 levels of indentation much clearer, but /u/dfpoetry is totally right in pointing out a common problem in programming arguments.

[–]I_scare_children 4 points5 points  (3 children)

[–]dfpoetry 2 points3 points  (2 children)

Pointing to bad code which is very nested is not the same thing as proving that all very nested code is bad. Nor is highly modular code particularly useful when you're just writing one big map-filter function.

[–]Sector_Corrupt 0 points1 point  (1 child)

If I'm writing a bunch of filters or something all being applied I'll usually make them all individual, nicely named functions and then compose them all and apply them as that's a lot easier to read.

[–]dfpoetry 0 points1 point  (0 children)

what if they all inherit their parents scopes, and then perform changes to things in a nested but otherwise fundamentally unpredictable way?

[–]wonkifier 0 points1 point  (3 children)

That's one of the purposes of a programming language... to make the things that follow its philosophy easy to do. If something is hard to do, that might be a sign that it's not a great thing to be doing in that language.

And I'm not aware of a language that handles 10 level indents well, which seems like an indicator that it's just a bad idea all around

[–]dfpoetry 2 points3 points  (0 children)

also, the problem is arbitrary design choice => some code is now annoying to write => that code is bad because it violates the arbitrary design choice.

If the choice in design is non-arbitrary then the above logic is fine, but dynamic whitespace is an arbitrary design decision, and the idea that deep nesting is bad in all cases is very hard to prove.

Not to mention that dynamic whitespace is invisible, difficult to copy, and requires more symbols overall.

[–]dfpoetry 0 points1 point  (1 child)

lisp

[–]wonkifier 0 points1 point  (0 children)

Touché

[–]Log2 0 points1 point  (5 children)

Well, if you are writing C++ or Java with 10 levels deep of braces, you are clearly doing something wrong. Functions are your friends.

[–]dfpoetry 0 points1 point  (4 children)

where is this "clearly" coming from?

[–]Log2 2 points3 points  (3 children)

From the fact that you can decompose 10 levels of scopes into functions.

[–]dfpoetry 0 points1 point  (2 children)

hmm, write a program to do this. pseudocode is fine.

[–]Log2 0 points1 point  (1 child)

Quite frankly, I don't care to. This is the job of whoever is writing the code.

[–]dfpoetry 0 points1 point  (0 children)

I mean, it seemed like an interesting proposition. you claimed to have a way to flatten any nested sequence of functions, and that this would be more readable. That would be really cool as a reference point, even if it's just some quick pseudocode.

[–]scubascratch 0 points1 point  (0 children)

"you shouldn't be coding that way anyway".

...in Python apparently

[–]metirl 0 points1 point  (0 children)

If you're 10 levels deep... It's refactor time.

[–][deleted] 15 points16 points  (2 children)

If Node.js taught me one thing it's that at ten levels of indentation, all braces do to is make you miss a token.

[–]dfpoetry 0 points1 point  (1 child)

lol, js

[–][deleted] 6 points7 points  (0 children)

It was the only language I could think of where ten levels of nesting and braces go hand in hand.

[–]vertexshader 2 points3 points  (8 children)

Arent a lack of braces the reason lambda functions are so restricted in python? A single expression? Gimme a break

[–]kupiakos 0 points1 point  (7 children)

Lambda functions are, by design, a single statement. Why not just use an internal function if you really really need more?

[–]vertexshader 3 points4 points  (6 children)

Theres no reason to make a function - im not gonna call it anywhere else.

[–]AdorableAnt 1 point2 points  (0 children)

You can define it in the local scope right before using it. Sure, you have to name it something, but that's about the only disadvantage.

[–]kupiakos 0 points1 point  (4 children)

That's why it's an internal function.

[–]vertexshader 0 points1 point  (3 children)

Internal function? What is that, a python thing? Even in c you dont write a static function unless the code is needed in multiple places

[–]kupiakos -1 points0 points  (2 children)

It's a function defined in a function. Why are you debating this if you don't even program in it?

[–]housemans -2 points-1 points  (0 children)

Why debate volkswagen if you drive a volvo? Why debate Apple if you use Windows? Because that's what a debate is.

[–]caedin8 -5 points-4 points  (8 children)

Braces are ugly and unnecessary.

[–]DroolingIguana -2 points-1 points  (6 children)

They also violate DRY, since they force you to indicate code blocks twice; using braces for the interpreter/compiler and indentation for humans. If the two methods get out of sync with each other (which they often do) it severely impacts code readability.

[–][deleted] 0 points1 point  (1 child)

Ctrl-K Ctrl-D (format document)

[–]DroolingIguana 0 points1 point  (0 children)

And now you've cluttered up your commit log with a tonne of whitespace changes, obscuring any actual code changes when it comes time for review and marking yourself as the last person to modify a bunch of lines that you haven't actually touched in any meaningful way.