
Why most “clever” code ain’t so clever after all - partisan
https://drive.google.com/file/d/0B59Tysg-nEQZOGhsU0U5QXo0Sjg/view
======
belovedeagle
This article hinges on a rather silly fallacy. Luckily the author does us the
favor of making his mistake very clear in the curve-fitting analogy; but first
look at the code.

He gives several examples of poorly-written FP code (which we're supposed to
believe is "clever" by virtue of being FP), then introduces some new problem
constraint which is carefully _selected_ to make the FP solution break but
some equally ridiculous imperative-style code not break. In fact, what he
really wants to say is, "FP is stupid", but he won't come out and say it.

This is illustrated perfectly by the curve-fitting analogy: a perfectly-fit
5th degree polynomial is the "clever" solution, but oh wouldn't it be so much
simpler to use an best-fit 3rd degree function? And oh look, the 3rd-degree
polynomial has a positive slope at the end whereas the 5th degree polynomial
has a negative slope, ergo if we _just happen to pick a point greater than the
previous one_ , ta-da! The 3rd-degree polynomial fits the new set better than
the 5th-degree. QED.

When put that way, the argument is completely idiotic. You could just as
easily have picked a new point lying, say, _on_ the 5th-degree polynomial and
thus "prove" that it was a better fit for some yet-to-be-defined data than was
the "non-clever" 3rd-degree polynomial. The analogy to the code is that it
would be trivial to come up with new requirements which could be easily solved
by the "clever" FP code and very difficult by the imperative-style code; and
in fact the author admits as much in the second case.

By the end I can't help but wonder if this was satire, designed to make anyone
agreeing with the article look silly when the author later comes out pointing
out all the above...

~~~
kirrent
As a physics grad that curve fitting section was hard to read. Only very low
degree polynomial fits can ever be used, through some coincidence with the
underlying trend, for extrapolation. A polynomial fit is a simple and easy
method to fit a curve which you can use for interpolation if you want. Unless
you have some clearly identifiable trend, then extrapolation doesn't mean
anything no matter what curve fitting method you use.

It's like criticizing a Taylor series for diverging from the target function
as you get far away from its centre. No shit that'll happen. It's the name of
the game.

I do have to agree that that section read a lot like satire.

~~~
GFK_of_xmaspast
You can get away with high-degree polynomial interpretation if you have
control over the node locations, see for example Nick Trefethen's chebfun:
[http://www.chebfun.org](http://www.chebfun.org) and book:
[https://people.maths.ox.ac.uk/trefethen/ATAP/](https://people.maths.ox.ac.uk/trefethen/ATAP/)

------
sbov
Some like to say that you should never write clever code. But clever to who?
Some people lambdas, map, reduce, and even polymorphism are clever. So in a
vacuum the statement is meaningless.

Whenever you're writing code you need to know your audience. Is the Haskell
solution really that clever to someone who uses Haskell on a daily basis?

That said, I always liked the saying: it's easier to write code than read it.
Which means, that if you're writing code at "maximum cleverness" you're
writing code you won't be able to read.

~~~
Consultant32452
My rule of thumb is that I attempt to write code that a junior programmer
whose experience is limited to a little more than having taken a course in the
language and has done a couple tutorials on the framework(s) can maintain. I
see so many developers try to squeeze every new language feature or loads of
esoteric library use cases into their code because they learned something and
feel like it MUST belong in the production code. Sometimes you have to write
complicated code, but 99% of the time it should be possible to write very
simple code using only the most commonly used language features.

~~~
jdmichal
For all of Java's pitfalls, this is one of the things it really has going for
it. The language itself is extremely simple. I think adding properties instead
of the weirdness of getters and setters would take it to peak simplicity and
straight-forwardness. (Also, maybe eliminate inheritance and force interfaces
and composition instead.)

~~~
mohaine
Adding properties would just make the language MORE complex. Then you would
have fields, getters/setters AND properties. That said, a "property" keyword
that auto-generated default getters/setters would be nice.

This was one of things that always bothered me about C#, What is the point of
properties. What do the offer that public fields don't?

~~~
tigershark
You don't need getters and setters in c#. If you don't have any side effect in
a read-write property you can just use an automatic property. The same for
read only properties that don't need to be recalculated on the fly. And I hope
that I don't need to explain why using a write only property is almost always
a code smell. In the other cases you can use a normal property with a backing
field. If your code is well written and you use the appropriate tools, only
rarely you need normal properties (that are as annoying as getters/setters).

What do getters and setters offer that public fields don't? Properties offer
exactly the same advantage as getters and setters, fields encapsulation
[https://en.m.wikipedia.org/wiki/Field_encapsulation](https://en.m.wikipedia.org/wiki/Field_encapsulation)
No wonder that you don't understand properties if you don't know why getters
and setters are useful.

------
squeaky-clean
I understand what they're getting at, but I think the final listing is even
more "clever" than the first. The first one I could read and understand pretty
easily, and I've never written Haskell. The few things it does, and how, are
easy to spot. The final one is harder to understand at a glance, even though
it's more generalized. Some people take it even further than that, to where
the implementation is totally abstracted from it's only uses.

If you removed the "fizzbuzz" related names from the code, could you tell that
listing 4 is being used for a FizzBuzz? Sometimes a 3&5 FizzBuzz is just a 3&5
FizzBuzz and nothing more, and doing anything further is akin to premature
optimization. Planning is hard, haha.

I realize FizzBuzz is a toy solution for the sake of example, and the solution
with fewer assumptions is more often the better choice when dealing with
larger problems, overall this was a very good article. But we also need more
programmers to understand that if you're writing an internal backend function
which is passed a date-as-integer, you probably don't need to double check
that it's actually string representation of the integer, or an ISO formatted
date, or YYYY-MM-DD formatted string, or YY-MM-DD formatted string, or....

~~~
cbrogliato
The FizzBuzz example was just to underline how a "considered clever" solution
for an academic problem behave when moved into "real life" situations. If we
are taught to solve and think problems as if we were in an idealized world we
are going to suffer once we enter the battlefield.

------
iagorodriguez
I run a small dev company. This is the first lessons we teach our programmers.
If your code cant be understood by someone else with minimal efforts it is not
good enough.

~~~
TeMPOraL
Do you maintain a high-enough level within the team though? Most "clever code"
I've seen is called clever by people who are not yet proficient enough in a
language to understand it. So with unskilled programmers pretty much anything
that isn't straight combination of classes, loops and conditionals will be
considered clever. There's a tradeoff here - I get the business reasons why
one may want to have the codebase at the "lowest common denominator" level for
the team. But then again, programming is a craft, so that person who says
lambdas in Java are "clever code" (real example from my work) should IMO suck
it up and spend few hours learning; then the code will stop being "clever" to
them.

~~~
slededit
Not every company wants highly skilled programmers. Depending on the
complexity of the project, and cost of defects unskilled programmers may be
the most cost effective.

~~~
TeMPOraL
I know. Took me a while to realize it and understand why companies think that
way, but it boils down to the fact that as programmers, we're not paid to do a
_good_ job, we're paid to do a _good enough_ job. Those two goals are very
often at odds. It seems to be a typical clash of craftsmanship with market
economy.

~~~
jbattle
Another articulation is - we aren't paid to write code, we are paid to build
stuff.

Authors get paid to write prose people can read - the readability of our work
only 'matters' insofar as good craftsmanship practices help the team maintain
velocity over the long haul.

------
z0r
You can code yourself into a corner no matter what language you use. I've not
seen much of this beautiful many-seamed code that could suddenly accommodate
unanticipated feature requirements without rearchitecture, but I have enjoyed
a certain amount of seamful boilerplate requiring much burnishment as changes
are made. I don't think I'm anywhere near as experienced as the author but I
still think this article is setting up strawmen and knocking them down.

------
mpweiher
Code can be clever to allow (significantly more) other code to be simple.

For example, we are using Higher Order Messaging[1] to turn this:

    
    
       if ( [delegate respondsToSelector:@selector(doSomethingElse:)]) {
          [delegate doSomething:self];
       }
    

into this:

    
    
       [[delegate ifResponds] doSomething:self];
    

Yes, the implementation of -ifResponds is slightly clever, though I've gotten
it simpler over time, but it certainly is worth it in the simplification of
client code.

Engineers manage tradeoffs. That's what makes them engineers.

[1]
[https://en.wikipedia.org/wiki/Higher_order_message](https://en.wikipedia.org/wiki/Higher_order_message)

------
stcredzero
Whether or not something is "clever" as opposed to annoying or obtuse depends
heavily on context. In the context of a production programming environment,
it's all about cost/benefit. Does it reduce the team's cognitive load? At what
cost?

------
pdkl95
Software _design_ is often about finding the correct balance between the
available implementation methods. Trading memory for time (low-mem/slow
recalculation vs higher-mem/fast precomputed-table) is the usual example.

Software design involves a similar trade-off where simple (but repetitive)
code can be traded for a smaller (and maybe more complex abstraction). It's
not about "cleverness" (though that can be a strong warning sign), but instead
if the abstraction involved is correct, useful, and appropriate.

> My code in Listing 4 is not based on those assumptions.

Yes. It's based on the _new_ assumption that it might be necessary to "add
that pair to the list of cases" in the future.

> I choose not to read into requirements _more than needed_.

Except for the new abstraction that attempts to allow for other word
replacements not mentioned in the original specification.

While I agree that hard-coding assumptions is usually a bad idea, soft-
coding[1] can also be a problem.

> FP

> OO

That's the Expression Problem[2], which always involves yet another trade-off.
Some types of modifications are easier under FP, others are easier under OO.

[1]
[http://thedailywtf.com/articles/Soft_Coding](http://thedailywtf.com/articles/Soft_Coding)

[2]
[http://c2.com/cgi/wiki?ExpressionProblem](http://c2.com/cgi/wiki?ExpressionProblem)

------
ezyang
I didn't make it past the fizzbuzz example. But I wouldn't call the solution
with cycle all that clever. Here's a better one:
[https://themonadreader.files.wordpress.com/2014/04/fizzbuzz....](https://themonadreader.files.wordpress.com/2014/04/fizzbuzz.pdf)
the punchline reproduced below:

    
    
      fizzbuzz' :: Int -> String
      fizzbuzz' n = (test 3 "fizz" . test 5 "buzz") id (show n)
        where
         test d s x | n `mod` d == 0 = const (s ++ x "")
                    | otherwise = x
    
      fizzbuzz :: [String]
      fizzbuzz = map fizzbuzz' [1..]
    

The article is very interesting and worth a read. In particular, this
implementation doesn't come out of "thin air": it's been golfed from an
implementation that defines a simple domain specific language to solve
FizzBuzz. And if you understand your problem well enough, DSLs are an extreme
force multiplier.

~~~
squeaky-clean
Feature request: Client should be able to specify either 3, or 1023 for the
number needed to print "fizz."

Feature request: Client should be able to optionally request "Lizard" as
another string whenever the number is a multiple of 13.

That's what the article is referring to. Your function may be more clever, but
it's more specific, works in only exactly the requested cases and is harder to
change.

~~~
mmierz

      fizzbuzz' :: Bool -> Bool -> Int -> String
      fizzbuzz' big doLiz n = (fizz . test 5 "buzz" . lizz) id (show n)
        where
         test d s x | n `mod` d == 0 = const (s ++ x "")
                    | otherwise = x
         fizz = if big then (test 1023 "fizz") else (test 3 "fizz")
         lizz = if doLiz then (test 13 "Lizard") else id
    
      fizzbuzz :: Bool -> Bool -> [String]
      fizzbuzz big doLiz = map (fizzbuzz' big doLiz) [1..]

------
johnfn
Cycled lists and the modulo operator are two different abstractions for
thinking about the same problem, in the same vein of "blind men grasping at
elephant." They are different perspectives on the similar problem.

The reason we think that cycling a list is "clever" but using modulo is
"boring" is because most of us use modulo often, and cycling rarely. There is
nothing inherently "clever" about either of the two approaches.

In an alternate universe where we all used Haskell, perhaps the modulo
solution would come off as "clever". And perhaps this article would be
pointing out the flaws in the modulo approach instead: the fact that numbers
divisible by both 3 and 5 have to be treated by a special case, the fact that
it doesn't easily generalize to fizz buzz bazz, etc.

~~~
emn13
The issue with the first fizzbuzz isn't just cylcling lists, it's also the use
of zipWith. At least, that's what causes some of the brittleness in his
examples.

\---

Also, I think you're _slightly_ wrong in that cycled lists and modulo are
"just different abstractions" \- they may be to a machine, but they are not to
people. And in this case, critically, the _original problem 's phrasing_ is
much closer to the modulo implementation than the cycled list equivalent:
"...for multiples of three print Fizz...".

By choosing to _encode_ that constant 3, rather than represent it, you're
being clever; by failing to use a straightforward analog of "is multiple of",
you're being clever.

This "encoding" has real risks - for instance, because you're human, you'd
immediately notice that 5 is not 6. You don't need to think to do that; don't
need to process equivalence or do any kind of algebraic reasoning; a kid can
and will notice the identical shapes before they can talk or walk. It's built
in. By contrast, even a trained professional might overlook a list with _5_
spaces followed by Buzz as opposed to a list of _5_ elements consisting of
spaces followed by a single Buzz. Sure, it's a really stupid mistake, and one
I hope you make very rarely - but _people make stupid mistakes_. No need to
encourage that habit by making them think.

So no, it's not that cycled lists are bad and module is good, it's that the
problem description is closer to the definition of modulo than it is to list
cycling. (This is my interpretation, the article's definition of clever is
more about seems and changes and doesn't really touch this).

~~~
groovy2shoes
Modular arithmetic (a.k.a. clock arithmetic) is equivalent to cycles in a very
real, mathematical way. There's a reason that clocks are round -- in order to
count the naturals modulo 12 (and again modulo 60), you simply need to cycle
1..12 (or 1..60). I'd argue that for anyone who understands modular
arithmetic, the `cycle` solution doesn't seem particularly clever. Anyone with
enough knowledge of number theory to understand that " _x_ is a multiple of
_y_ " -> " _y_ divides _x_ " -> " _x_ (mod _y_ ) = 0" ought to have an
understanding of the relationship between modular arithmetic and cycles. This
is introductory-level discrete mathematics.

------
daxfohl
Your API should strive to be clever (it should allow end users to do countless
things straightforwardly without error). Your implementation should not.

~~~
gohrt
What does that mean? How is that clever?

------
curuinor
C. Alexander in _Notes on the Synthesis of Form_, rather explicitly puts forth
the essential architectural problem in a semiformal form - a constraint
satisfaction problem.

The closest application of CSP-like problems in places close to statistics-
land is in neural networks, where Rumelhart published in multiple places that
the objective of neural networks, including the backprop net that he
reinvented, was soft CSP. So actually, there is probably a path towards formal
equivalence.

Of course, there's a disgustingly large amount of work done on the analogies
between CSP and Ising model at criticality, because you only need to look at
the damned things (compare to Hopfield model). The other path is to look at R.
Sole's work on munging for power law-ish things in software. So physical
methods for software are actually quite old hat (from the 90's). It's just
that it's all really fucking hard condensed matter stuff.

This is quite close to Huberman's work at PARC about networks and stuff, which
led to the different Adamic and Huberman munging for power laws in networks
and Internet things.

------
wangchow
So long as the "cleverness" is well-documented it's not a problem. Authors
should explain why it is written the way it is. Problem solved.

I've seen some weird-ass calculations in code with no comments. If the author
has gone through so much effort to come up with the clever solution why not
add some commentary so the next guy can read it quickly.

That's why every language has support for comments..

~~~
yoklov
OTOH comments easily become outdated, and it's painfully easy to make a change
that subtly invalidates a comment in another file. They're like lines of code
that is never tested or run.

For calculations, I'm not really sure I agree either. I used to work in games,
specifically on physics and collision, and there's a point at which you have
to assume that the person maintaining it has a reasonable level of familiarity
with the subject matter, or else you'd need to write a comment that contains
all of a linear algebra course.

~~~
bigger_cheese
I do not see this as an issue if the comment immediately Precedes the "Clever"
section of code.

Calculations are the most important part of the code to comment I'm an
Engineer most of the guys I work with have never been formally taught any
programming often I see a bunch of code written in Fortran with notoriously
short variable names if it wasn't for comment very easy to get lost.

So long as there is a comment saying something like

"Ergun's Equation - Calculates Pressure Drop"

You don't need to follow all the fluid dynamics to know what the code does -
at minimum someone reviewing it can google "Ergun's Equation"

You don't need to comment linear Alegbra but at least comment what the code
does "//Solve using LU factorization" or similar is sufficient and it beats
seeing segments of code like this with no explanation:

r[i] = 1/(p _b[i]+t[i]-f_ r1[i-1]-g _r2[i-2]); u[i] = a[i]-r1[i-1]_
u[i-1]-r2[i-2] _u[i-2]; f = p_ c[i]+t1[i]-h*r1[i-1];

Edit: It stripped chunks of the above code but you get the idea.

~~~
wangchow
True--sometimes when translating from a mathematical context to programming
causes some readability pains! Especially if it's something that takes
advantage of multiprocessing to solve the problem.

------
emblem21

      for(var i in list) { 
        (function(element) {
          someAsyncCallThatNeedsTheRightIndexedElement(element);
        })(list[i]);
      }
    

I've seen people grumble that this is " _too clever_ "

I've been known to snap back " _Bootcamps don 't have a monopoly on what
technical solutions are valid_"

Pointers are probably too clever for most people.

Admittedly, _goto_ is too clever for me. :(

~~~
atoko
Try using .call

~~~
emblem21
The binding of the element to the lexical scope is done for later executions.
.call is immediate.

~~~
Jach
I think they meant try using .call and see the 'too clever' remarks come in.
I'll delete this if they clarify...

------
echaozh
Hmm, it may not be very helpful, but there's a replicate function[1] right in
the Prelude. I always thought that being clever in Haskell is to be able to
find all the strange but powerful packages and functions and weave them in as
tight a program as possible. Having to implement replicate by hand is
definitely not clever from the start.

Hmm, it's also what drove me away from Haskell and kept me on and off with the
language, as most of the powerful stuff are too powerful and so hard to
tame...

[1]
[http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude...](http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:replicate)

------
jorams
I think the real problem with the fizzbuzz example is that the "clever"
solution fails to properly break the problem down. Fizz buzz essentially
consists of two parts:

1\. We have an increasing sequence of numbers.

2\. Every number divisible by 3 is replaced with "fizz", every number
divisible by 5 is replaced with "buzz", every number divisible by both is
replaced with "fizzbuzz".

If you set out to solve these two parts individually, you would never come up
with the clever solution.

While the desired result of fizzbuzz is a specific sequence of numbers, that
fact is almost incidental. The clever solution doesn't really look at the
problem definition but instead merely generates the desired sequence.

------
qwertyuiop924
This is a fairly well known thing in all programming communities that matter,
but it was good to see it pointed out once again.

However, I would argue that the classic guarded function using modulo would
actually be clearer. It takes a moment to follow the logic of cycling up an
infinite list, and modulo statments are far clearer and easier to understand
to most people, I would say. In addition, it's easier to change than either of
the above, and equally declarative: modulo is a declarative operation as is a
guard: when x % 3 is zero, it IS fizz.

~~~
frank_jaeger
What about when x is 15?

~~~
qwertyuiop924
What about it? You make it a higher precedence guard.

------
junto
As a company we use C# and Visual Studio as an IDE.

Everyone has Resharper, which is undoubtedly an invaluable tool. However,
Resharper had a feature that will convert for/for each loops with conditional
logic into LINQ lambdas.

For simple cases I find it's conversion useful and readable, but for more
complex cases it becomes an unreadable nightmare for anyone bug fixing later
on. I find that to be the case even when I personally wrote the original code,
so I avoid these conversions whenever they become overly complicated.

~~~
kylemuir
Thankfully, it also has the same "Alt-Enter" to revert said LINQ query :)

~~~
junto
Exactly, but colleagues also have to be encouraged not to always trust what
Resharper suggests!

~~~
kylemuir
Definitely agree - my pet peeve (although I haven't seen it for a while) is
introducting circular references by adding compiled dlls from /bin/debug as
references instead of adding a project reference.

I use to see it a lot a few years ago, maybe I just got a little better at my
job?

------
mannykannot
There is an interesting genre of complexity that is justified (by its
creators) on the grounds of simplicity. One sub-genre makes reducing the line
count the primary measure of simplicity. Another sub-genre aims for the most
generic and flexible code in order to simplify reuse and modification,
regardless of the chances those capabilities will ever be used, or whether
several different specific programs would be the simpler, faster and cheaper
solution.

------
cwbrandsma
I remember the days of seeing inline assembly in code (delphi). We griped
about that code quite a bit, but this was in the days before "you can't
outsmart the compiler". Was it clever: yes. Was it maintainable: no. Was it
necessary: YES! This was drawing code, doing it via the normal language
routines was just not a performant solution.

------
partisan
Not the OP of the article. Just wanted to get perspective from Haskell devs on
the points raised in the article.

~~~
rev_null
I find it interesting that he dismisses the use of Maybe. The monadic solution
isn't clear, but when used as a Monoid, it's clear and easy to modify:

[https://gist.github.com/anonymous/7a1829f61cf9302c188f80a736...](https://gist.github.com/anonymous/7a1829f61cf9302c188f80a7366e9803)

------
ovt
I wonder how common the use of "ain't" is today. I grew up in the rural US in
the 1980s, and it was common. And today I don't hear it, but I live in a
more... well, educted area. It throws me off when I see it in writing now.

~~~
azhenley
I think it is just more common to say it rather than write it (I too have
noticed this).

------
jheriko
i've often complained about "clever" code. simple code is easier to maintain,
and clever tricks are difficult to read...

speaking of difficult to read. please don't set examples with terrible single
letter variable names. and terrible two letter variable names. these are even
less forgiveable than clever tricks in my book. don't encourage abbreviating
things.

programming is about thinking about, reading and writing code a hell of a lot
more than it is about typing.

------
gohrt
Why post this draft to HN instead of letting the other make it more refined
and readable?

~~~
stites
I think it's because it hit "1.0"

------
hinkley
"There's a fine line between being clever and being a smartass."

------
stites
This article/paper/draft was a bit of a strange read. Just looking at
Experiment 1: it seems like he is taking weird / baseline-acceptable code for
fizzbuzz (perhaps warranting a PR to frege) and refactoring to make it more
flexible/production-friendly (overkill for fizzbuzz). He then argues that the
latter isn’t clever? Honestly, it seems too clever for fizzbuzz! If I asked
fizzbuzz in an interview and the candidate told me the final solution, I'd
think it was very clever and very overkill. I guess I don’t understand what he
means by clever!

I like that he made an attempt to make this language-agnostic, the final
example, but now he's introducing the idea of "elegance," (missing a
definition) and kind of saying that "cleverness == elegance == overfitting?" I
think the problem here is that he's _too_ generic (might I say, he's trying to
be too clever : P) and pulls in a third field to make his abstract
"cleverness" case. But, from my understanding, I'm under the assumption that
cross-validate is a given for models: overfitting is for chumps and students.
I'd give that a "to be improved" for draft 1.1.

\---

Overall, from the recap:

> “Clever” code tends to exploit explicit and implicit symmetries in the
> problem structure, building highly-symmetrical solutions by exploiting
> highly-symmetrical library functions / types.

and from the abstract:

> Unfortunately, people tend to assume that the style of such (supposedly)
> clever code is representative of “good” functional programming.

The main issue here is that clearly the first part is correct and makes for
fragile code (given context), but he’s completely opinion based on the latter.
What he needs to do is find “production-level code" or “education-level
practices” that are handed down to new FP developers and taken as “truths."
After reviewing the citations, I think that his points on [6] and [7] are
weak: he needs to show more than just comments and one-offs, and needs to
hammer in the "why this is bad" more. Instead, it feels like he's just trying
to show his contempt for haskell. I'm also thinking that, if he wasn't just
trying to run haskell through a ringer, you'd think he'd use other functional
languages / language-features (frege is designed as "a Haskell for the JVM").

His opening line of 'there is a deluge of “clever” code constantly pushed down
from gurus to hipsters to innocent readers' also primes me to think that this
is more of a cathartic article than a useful one.

I'd love to agree with him, say that "stupid code, everywhere" is a problem in
educating new devs for pure-functional languages, then spider the internet,
comment/fix all these code examples, and finally make all my friends haskell
developers : P (disclaimer: I'm just an enthusiast). However, I remain
unconvinced — I think the problems in haskell education is elsewhere. Perhaps
I have simply been lucky in finding sane, good resources, and all the
production code I study is uncommonly blessed. Looking forward a 2.0 so that I
can convert all my friends.

(I redacted some more thoughts since this is my first real post to HN; be
nice!).

