Live data from Hacker News

Clever code is probably the worst code you could write (2023)

read.engineerscodex.com

171–180 of 204 posts

Re: Clever code is probably the worst code you could write (2023)

#171
post #4

I also find that, in C++, int sum = 0; for (int i = 0; i is a lot easier to understand than return std::accumulate(x.begin(), x.end(), 0, [](int a, b) {return a + b;}); Yet, the latter is considered more correct and better, with static analysis like cppcheck telling you to use the latter. It does have many advantages, like no mutable variables lying around, but gee it is annoying to read.

That is just a question of what you are used to. The "functional style" example is very obvious to a person who is familiar with that style. In fact if the rest of the code base is in a similar style it is the easier one to understand.

There is nothing inherent which makes either better or cleverer than the other.

(Also the first one is incomplete, as "n" is not defined, making the later more self contained)

Re: Clever code is probably the worst code you could write (2023)

#172
post #32
post #10

Earlier quoted context omitted.

We now have return std::reduce(x.begin(), x.end()); Which is a little cleaner and is even faster (compiler is free to do the additions in any order). https://en.cppreference.com/w/cpp/algorithm/reduce - it looks like even the `accumulate` example can be made simpler with `std::plus`. I prefer the `reduce` option for a number of reasons, but understand why someone might not.

That's terrible! The main operation, addition, is completely hidden magic! But the completely trivial calls to .begin() and .end() are explicit! Why would I want to add by default?

What is hidden? It's a reduce on + starting with 0, hardly anything subtle.

Re: Clever code is probably the worst code you could write (2023)

#173
post #4

I also find that, in C++, int sum = 0; for (int i = 0; i is a lot easier to understand than return std::accumulate(x.begin(), x.end(), 0, [](int a, b) {return a + b;}); Yet, the latter is considered more correct and better, with static analysis like cppcheck telling you to use the latter. It does have many advantages, like no mutable variables lying around, but gee it is annoying to read.

That is somewhat annoying, yes. I'm a huge fan of Python's list comprehension, but it's generally accepted to be a normal part of the language, and IMO, more readable: print([x for x in range(10) if not x % 2]) [0, 2, 4, 6, 8] vs. l = [] for x in range(10): if not x % 2: l.append(x) print(l) [0, 2, 4, 6, 8]

With ruby 3.4 (prior to that `_1` should be used instead of `it`):

    (0..10).select{it.even?}

Re: Clever code is probably the worst code you could write (2023)

#175

Earlier quoted context omitted.

That is somewhat annoying, yes. I'm a huge fan of Python's list comprehension, but it's generally accepted to be a normal part of the language, and IMO, more readable: print([x for x in range(10) if not x % 2]) [0, 2, 4, 6, 8] vs. l = [] for x in range(10): if not x % 2: l.append(x) print(l) [0, 2, 4, 6, 8]

With ruby 3.4 (prior to that `_1` should be used instead of `it`): (0..10).select{it.even?}

Also not that the result is `[0, 2, 4, 6, 8, 10]`, unlike the behavior of Python which exclude the value explicitely passed in parameter from the represented range.

Re: Clever code is probably the worst code you could write (2023)

#176

Earlier quoted context omitted.

I think there's a difference between "clever" and complex. You can express a complex algorithm or pattern with simple easy to understand code - complexity doesn't have to manifest itself as unreadable or incomprehensible code. To me "clever" code is more about they way you are doing something than the complexity of what you are trying to do. Clever is the opposite of straightforward and easy to comprehend without a d…

I also prefer this perspective. I had an epiphany about this sort of code when I was trying to describe what my code did for a research paper, and in trying to express why I was proud of it, I called it complex, only for my research advisor to point out that complexity was not the point of the work, so calling it complex did not convey what about it made the research interesting. Although it wasn't his intention, it…

Just to codify this with some examples: Here’s some recent examples of what I consider “clever” that I’ve had to work with from previous people and that I’ve written myself:

Someone who loved Lisp wrote a bunch of the unit test suites where I work using Python in a very clever metaprogramming way. They would dynamically generate and attach functions to a test object for testing REST requests. This is both

1. Difficult to read and understand

2. Much more difficult to test the behavior of

All to save probably maybe 100 lines of code. This is an example where I feel like code is too clever for its own good without having a good reason to be like that. It also flies in the face of what you would conventionally expect when it comes to Python unit test suites.

One example where sometimes it’s necessary to be clever: I did a db migration in about 100 lines of Python/SQL that worked fine at small scale using Alembic/Python/SQL and was a straightforward update with CTE. When tested on large production grade dataset however it completely fell apart. Some clever hacks with batching and temp table later and I have something runnable, but now it’s all in sql and while well commented is much harder to grok what’s going on at first glance.

Re: Clever code is probably the worst code you could write (2023)

#177

I wonder how old this advice is. I know that it predates the century but I'm not sure how far back. (A favorite quote of mine that I believe is from the 1970s says "It's easier to make working code fast then to make fast code work." Since the fundamental problems of programming have not changed over the centuries, I wouldn't at all be surprised if there's an anti-clever saying from the dawn of computing)

> the fundamental problems of programming have not changed over the centuries It hasn't been a whole century yet. Grace Hopper's career started in 1944, eighty years ago.

Ada Lovelace predates her

Re: Clever code is probably the worst code you could write (2023)

#178
post #133
post #120

Earlier quoted context omitted.

I think it's mostly a fad issue. Normal loops and if statements are just as possible to hit with a static analyzer. But the very fact that they look easy makes a certain kind of programmer see them as beneath them. They want the complex looking code, even if it's functionally equivalent and semantically no more sound. They like the visual noise and complexity of it. It rubs their egos the right way.

Loops are more complex. They expose more implementation details, worse, they "expose" irrelevant details. Unless your CPU is very simple, like a Cortex M0, the C compiler will likely rewrite your loop using vector instructions (think MMX / SSE / Neon), leaving an unrecognizable mess where a neat loop with an index used to be. C was invented to match PDP-9 and PDP-11, and it matches them beautifully. Constructs like *…

    00106   template
    00107     _Tp
    00108     accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
    00109            _BinaryOperation __binary_op)
    00110     {
    00111       // concept requirements
    00112       __glibcxx_function_requires(_InputIteratorConcept)
    00113       __glibcxx_requires_valid_range(__first, __last);
    00114 
    00115       for (; __first != __last; ++__first)
    00116     __init = __binary_op(__init, *__first);
    00117       return __init;
    00118     }
    
    https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.0/stl__numeric_8h-source.html#l00108
It's just a loop.

The compiler is doing all of the same transformations regardless of whether you use the higher order function or just write a loop yourself.

--------------

    _STD_BEGIN
    _EXPORT_STD template 
    _NODISCARD _CONSTEXPR20 _Ty accumulate(const _InIt _First, const _InIt _Last, _Ty _Val, _Fn _Reduce_op) {
        // return noncommutative and nonassociative reduction of _Val and all in [_First, _Last), using _Reduce_op
        _STD _Adl_verify_range(_First, _Last);
        auto _UFirst      = _STD _Get_unwrapped(_First);
        const auto _ULast = _STD _Get_unwrapped(_Last);
        for (; _UFirst != _ULast; ++_UFirst) {
    #if _HAS_CXX20
            _Val = _Reduce_op(_STD move(_Val), *_UFirst);
    #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
            _Val = _Reduce_op(_Val, *_UFirst);
    #endif // ^^^ !_HAS_CXX20 ^^^
        }
        return _Val;
    }
    
    https://github.com/microsoft/STL/blob/63354c3fa9c1fb2ab1fccb58c47d23c6af1c290f/stl/inc/numeric#L24
Also just a loop in MS' standard lib.

Re: Clever code is probably the worst code you could write (2023)

#179
post #99

Earlier quoted context omitted.

> and is even faster (compiler is free to do the additions in any order) Is that actually true? I'm not even sure how hypothetically removing ordering requirements would help you extract performance, let alone any compilers that could do anything with that today. Unless the standard library were to auto-parallelize the reduction, but I doubt they'd do that because the overhead of starting threads would be quite costl…

I think relaxing the ordering requirement let's you use simd something like this (semi-pseudo code) (a, b, c, d) = (0, 0, 0, 0); for(int i = 0; i

What if n is not a multiple of 4? There is a trick I can't recall at the moment for handling this with a case statement on n mod 4 at the end to wrap this up.

Re: Clever code is probably the worst code you could write (2023)

#180
post #99

Earlier quoted context omitted.

> and is even faster (compiler is free to do the additions in any order) Is that actually true? I'm not even sure how hypothetically removing ordering requirements would help you extract performance, let alone any compilers that could do anything with that today. Unless the standard library were to auto-parallelize the reduction, but I doubt they'd do that because the overhead of starting threads would be quite costl…

I think relaxing the ordering requirement let's you use simd something like this (semi-pseudo code) (a, b, c, d) = (0, 0, 0, 0); for(int i = 0; i

Are you sure that the compiler doesn’t autovectorize a simple loop into this form anyway? This is kind of the defacto scenario for that pass.
Post reply on HN