Showing posts with label tongue-in-a-cheek. Show all posts
Showing posts with label tongue-in-a-cheek. Show all posts

Apr 1, 2016

Happy Bleeding Edge

Good day to you, dear friends!

The upcoming C++19 standard is upcoming very soon, and the word is it's going to have many exciting new features!

Just to mention a few:

  • The language becomes garbage-collected. It means you never need to use "delete" anymore. For the backwards compatiblity, it's not an error if you still do, though
  • The curly braces syntax becomes deprecated (in an attempt to get rid of the "curly brace languages" club bad rep). The Python-like syntax is now preferred
  • One Definition Rule is now relaxed. You can define all you want; the winner is picked randomly at runtime using the Mersenne Twister
  • Going forward, friends are not anymore allowed to see each other privates
  • It is a compilation error to have tabs in the code. If the amount of comment lines in a file is less than 33.3%, it is also a compilation error (this should force good coding practices)
  • Substitution Failure Is Now An Error
  • Resource Acquisition Is No Longer an Initialization
  • The syntax for the main() function has been modernized (the correct syntax is now "public static void main ...")
  • A new modifier keyword, thread_safe, has been introduced. Insert it anywhere into your code to make it thread-safe
  • The standard library has been extended with many new useful algorithms. For example, std::fast_sort allows to sort arrays in a constant time (the precondition is that the input should be ordered)
  • Now it is OK to divide by zero, dereference NULL pointers and compare floating point values for equality: nothing bad will ever happen!

In order to keep our skills up to date and bleeding edge, let's practice some C++19.

I've prepared a little C++19 quiz for you:

What does this code do?

// note that this code requires a modern C++19 compliant compiler
#include <stdio.h>

int O = 5
char buf[6]
public static void main() thread_safe:
    float m[] = { 0.16437186, 0.57526314, 0.20729951, 0.13258759, 0.23461139 }
    for (int i = O; i --> 0;):
        for (int j = O, _= *(int*)(i + m); j --> 0; _>>=O):
            j[buf] = O*13 + (_&31)
        printf("%s\n", buf)

Enjoy!

NOTE: if you don't have an access to a bleeding-edge C++19 compiler, the code could be easily backported to a C++98 standard - just replace the Pythonic code blocks and Javaesque main() signature with an old C++ one!


UPDATE, APR 6 2016:

Posted on the 1st of April, the list of “new C++19 standard features” was indeed an "obviously non-funny" april’s fool thing.

Of course, there is no such thing as C++19, neither the committee would ever consider features as ridiculous as those mentioned (which is a shame, I would personally love to have tabs banished from existence :)).

As silly as the joke was, I tried to allude to a few topics there, in particular about “syntax vs semantics” in programming languages.

Or “form vs shape”, to put it in a philosophical perspective.

One can often hear arguments about the language syntax. The discussions are usually more heated at the levels of the “curly braces”, since this is the area where everyone is entitled to their own opinion (pretty much like on “tabs vs spaces”).

Let’s look at several forms of the same piece of code, in some hypothetical programming languages:

snippet 1:

for (i = 0; i < 5; i++) {
    for (j = 0; j < 5; j++) {
        println(i*j);
    }
}

snippet 2:

(for [i 0] (< i 5) (inc i) 
    (for [j 0] (< j 5) (inc j)
        (println (* i j))))

snippet 3:

for i = 0; i < 5; i++:
    for j = 0; j < 5; j++:
        println(i*j)

snippet 4:

for i = 0, i < 5, i = i + 1 do
    for j = 0, j < 5, j = j + 1 do
        println(i*j)
    end
end

snippet 5:

DO 1 i=0,4
   DO 1 j=0,4
      1 PRINT *,i*j

snippet 6:

For i = 0 To 5 Step 1
    For j = 0 To 5 Step 1
        Print(i*j)
    Next j
Next i

Which one is the “best”?..

Whatever your opinion is, let’s not forget that language syntax is purely a human, psychological construct!

In that light, it’s a straightforward code transformation to come back from the “C++19” syntax to a valid C++ syntax. We just replace Python-style code blocks with C++ curly braces, and of course remove the pseudo-Java main() function signature:

#include <stdio.h>

int O = 5;
char buf[6];
int main() {
    float m[] = { 0.16437186, 0.57526314, 0.20729951, 0.13258759, 0.23461139 };
    for (int i = O; i --> 0;) {
        for (int j = O, _ = *(int*)(i + m); j --> 0; _>>=O) {
            j[buf] = O*13 + (_&31);
        }
        printf("%s\n", buf);
    }
}

This code does not require a “C++19-compliant compiler” anymore. In fact, it compiles via a C99 compiler, runs and prints something!

It’s still not comprehensible, though. So let’s remove some more cruft, including the infamous “--> operator” and quirky C array indexing and try to find better names for the variables:

#include <stdio.h>

const int   BITS_PER_CHAR = 5;
const int   MASK = 31; // binary b11111, corresponding to BITS_PER_CHAR
const float WORDS_F[] = { 0.16437186, 0.57526314, 0.20729951, 0.13258759, 0.23461139 };
const int   NUM_WORDS = 5;
const int   NUM_CHARS = 5;

char buf[6];
int main() {
    for (int i = NUM_WORDS - 1; i >= 0; i--) {
        int bits = *(int*)&WORDS_F[i]; // reinterpret float value as int, bitwise
        for (int j = NUM_CHARS - 1; j >= 0; j--) {
            buf[j] = 'A' + (bits&MASK);
            bits >>= BITS_PER_CHAR;
        }
        printf("%s\n", buf);
    }
}

Now we can see (well, more or less) what does it try to do.

It extracts bit patterns (5 bits each) from the binary representation of the floating point values, interprets them as characters and prints that as words, one word per floating point value.

The floating point values are picked up specifically to encode some message.

Assuming that float is a single precision IEEE-754 floating point value (which most of the time is the case, but is not ultimately guaranteed), it is represented via 32 bits of data:

  • Mantissa in the lowest 23 bits
  • 8 bits for the shift-negative exponent
  • 1 bit for sign in the highest bit

Each character in the text is encoded via 5 bits, which is an offset from the character 'A' (i.e. 0 is 'A', 1 is 'B', 2 is 'C' etc.)

So in total each text word, such as "HAPPY", would take 25 bits. This is the whole 23 bits of mantissa plus cutting extra two bits into the exponent:

Note that unused exponent bits are padded with "011111", which is done in order to have the magic numbers look "nice":

So in the end, given a few assumptions we make about the current platform, the code would print:

HAPPY
APRIL
FIRST
RGRDS
CQUIZ

In addition to the "syntax argument fallacy", I'll throw in a few more maximas I was trying to allude to:

  • Garbage collection does not necessarily magically help to solve all the memory problems
  • It is essential to have a deterministic behaviour in the code
  • Thread safety is not something that can be achieved by magic, one still has to understand what's going on in a concurrent environment
  • Encapsulation is essential in design, but just having a "private" keyword in the language does not magically enable it
  • One has to know what are the complexity guarantees of the standard (or any other) library algorithms, if any. Otherwise it's just relying on magic (see above)
  • Certain things are fundamental to the way hardware works, and programming language can't be blamed for them not working "as expected"

There are many more programming language design related fallacies that people, myself included, may be subjected to.

I believe that working with such a complicated language as C++ does not necessarily make one a better programmer.

But it may, in a sense, force one into a certain awareness about such fallacies' existence.

Jan 11, 2010

Programmer's arrogance graph

We know that majority of programmers, even the most ingenious ones, are egotistical bastards, driven by arrogance.

Programmer's arrogance is both a blind, powerful driving force and also the reason for many failures.
For example, anecdotal evidence tells that initial programmer's estimate has to be multiplied at least twice to get the "realistic estimate":
Why are competent coders so bad at estimating? There are a number of reasons.  The main ones are:
  • Unforeseeable problems:
Many of the problems that come up during software development are unforeseeable.  If you have ever started a "simple" home improvement project and later found it was much more complicated than you realized...then you know first hand how programming can be...even for the experts.

  • Misunderstood/unclear requirements:
When the requirements are unclear, the programmer usually underestimates what it takes to build your software.  To use an analogy, they may estimate building your software as if it were a comfortable house.  Only mid-project do they realize that you were expecting the Taj Mahal! 
Or they just fall a victim to their own arrogance. Which is a bit simpler explanation.
Same stands for "Not Invented Here", "Invented here but not by myself", "My co-workers are all jerks" syndromes. And many more. Oh well.

Now, I've got a theory (which is built on empirical experience, of course), that if we imagine that amount of "arrogance" can be measured with a scalar value (let's call it a "magnitude of arrogance"), and if we try to build a graph of this value changing with the lifetime of the programmer, we might get pretty similar shape in 80% of cases:



The person (remember that we talk about future programmer) starts on some level of initial arrogance as a child/teenager, goes to school where everything is very new and unknown at first (point marked as "S" on the lifeline, which is horizontal).

But then he (not being sexist, just intentionally taking only males here) suddenly realizes that he's the "smartest kid in the class". The next thing he figures out is that his programming teacher "does not know a sh*t". Besides some boring, irrelevant and ages old stuff, that is. And sure he does not have any bleeding edge knowledge about, say, patching KDE under freeBSD.

Naturally, this skyrockets the level of arrogance significantly, and it keeps fluctuating somewhere at the top until our geek graduates and gets a job ("J").

Here the arrogance might drop down slightly again, because of the things being new and not familiar.
However, being used to the "best kid in the class" status, our soon-to-become programmer quite quickly catches up.

It might happen because of initial tasks being moderately doable and initial responsibility being not quite high, so this "gee, I can do the stuff" feeling warms up the ego.

Also, when coming to a corporate environment, young programmers often get a maintenance job in some legacy codebase. Knowing the nature of (most of the) legacy code bases, one would not get surprised if quite soon our kid gets to realize the thing.

See, it happens that all these "experienced" folks out there don't know a sh*t about programming either. As Gerald M. Weinberg puts it:
We often find material in programs that is [...] really present because of the history of the development of the program. For example, once the [...] function is changed to an [...] function, there is no longer any reason for the program [...] to appear. Nevertheless, things being what they are in the programming business, it is unlikely that anyone is going to delve into a working program and modify it just because the definition of [...] function has been changed. And so, some years later, a novice programmer who is given the job of modifying this program will congratulate himself for knowing more about [the subject] than the person who originally wrote this program. Since that person is probably his supervisor, an unhealthy attitude may develop - which, incidentally, is another psychological reality of programming life which we shall have to face eventually.
Soon he realizes, though, that things do not quite work as it appeared at first. There are several failures happening, which he fortunately realizes should be blamed on himself, and he suddenly faces the understanding that he knows, in fact, nothing about his job.

Then goes a long and painful learning, getting better and better every day, and finally getting to the next level of personal development, where one can look back and say: "see, I am not nearly as lame as I used to be when I started".

So we get another boost of overconfidence, which may or may be not followed by similar, multiple and abrupt drops and consequent slow raises.

What usually happens next is the point "P".
Which is promotion to the lead position (it does not seem that there is too many ways to get "promoted" in corporate programming business without becoming a manager), or becoming an entrepreneur, or changing the working place to work in considerably more "advanced" company. You name it.

The fresh feeling of power gives another arrogance boost. It only lasts that long, though, and soon again come disappointments.

And so on.

After many bumps on the road, if being lucky, passionate, persistent and introspective, one might slowly approach the level, which is marked as "H" on the vertical axis.

Which is "The Humble Programmer", as E.W.Dijkstra puts it:
We shall do a much better programming job, provided that we approach the task with a full appreciation of its tremendous difficulty, provided that we stick to modest and elegant programming languages, provided that we respect the intrinsic limitations of the human mind and approach the task as Very Humble Programmers.
The question is how close projection of this point upon the horizontal axis will happen to be to the point "R" (retirement).

And to the point "D", which comes next.

Jul 2, 2009

Kozma Prutkov, one of the brightest minds in the software development

Alright, that's quite a stretch. Not only Kozma Prutkov is by no means a software developer (he was a writer, one and a half centuries ago), it's also not a real person (but rather three people behind a single fictional name).
The reason that he's not that well known is that it appears to be quite hard to translate his aphorisms, which are hugely based on the wordplay. Which reminds of what Douglas Hofstadter said about Gödel, Escher, Bach translation:
There were a million issues to consider in any potential translation, since the book is rife not only with explicit wordplay but also with what Scott Kim dubbed "structural puns" - passages where form and content echo or reinforce each other in some unexpected manner, and very often thanks to happy coincidences involving specific English words.
And I think it's a shame in case of Kozma Prutkov. Those little revelations, hidden in a generally tongue-in-a-cheek perls of wisdom, in some ways defined my own world-view long time ago, and I am even more amused to see how do they manage to take new forms when I return to them today.
Fortunately, some people tried to translate it, anyway.
The good thing about this kind of ambiguous and satirical aphorisms is that one is free to put into them whatever meaning he feels fits better himself.
However, in this particular case I was really impressed to discover so many empirical truisms of the software development, which can be directly mapped upon Kozma Prutkov's aphorisms.
Well, this discovery is quite a pleasant exercise on its own. People tend to get enjoyment in finding familiar patterns observing seemingly unrelated phenomenas ("We experience delight when we recognize patterns, yet we're also surprised by them").
So, I will dare to cite some of them.
To be honest, my original plan was to append my own thoughts/associations (and possibly explanatory links) triggered by these sentences. But then I understood that most of those thoughts are pretty obvious, and besides that would possibly rob some people from having a kind of enjoyment I had while discovering these associations themselves.
So for only the couple of the first ones:
  • Do not cut everything that grows.
  • Buy a painting first, and then frame it.
You are not gonna need it
  • The first step of a baby is the first step to his death.
Most of the software designs are deemed to die much sooner than one would expect. But chances are that they will give a birth to the next generation, and many others after that. One has to consider it when designing the systems.
Now, I challenge you to find your own associations for the rest:
  • Many things are incomprehensible to us not because our comprehension is weak, but because those things are not within the frames of our comprehension.
  • If upon a cage of an elephant you will see a sign reading: "buffalo", do not believe your eyes.
  • When casting pebbles into water, look at the ripples being formed thereby. Otherwise this activity will be an empty amusement.
  • Never run to the extreme: who wants to dine too late today takes chances to instead breakfast tomorrow, early in the morning.
  • What other people can say about you if you can't say anything about yourself?
  • Trace everything back to the beginning and you will understand a lot.
  • New boots always pinch.
  • Nobody will embrace the unembraceable.
  • There is no great thing that would not be surmounted by a still greater thing. There is no thing so small that no smaller thing could fit into it.
  • Look in the root!
  • Better say little, but well.
  • People's memory is like a piece of white paper: sometimes it writes well, and sometimes bad.
  • What is the best? - Having compared the past, link it to the present.
  • If you have a fountain, plug it up. Let the fountain too have a rest.
  • Only reason on what your notions allow you to discuss. Thus, knowing nothing about the laws of the Iroquois language, can you pass a judgement on it that would not be ill-founded and stupid?
  • Having lied once, who will believe you?
  • Looking into the distance, you will see the distance; looking to the sky, you will see the sky; looking in a small mirror, you will only see yourself.
  • Where is the beginning of the end that comes at the end of the beginning?
  • Do not seek for unity in the whole, but rather in the uniformity of distinction.
  • We don't care for what we have, we deplore what we have lost.
  • Every thing is a manifestation of infinite diversity.
  • Who prevents you from inventing waterproof gun-powder?
  • When looking at the sun, squint your eyes - and you'll readily discern the spots upon it.
  • If all the past were present, and the present existed along with the future, who would be able to distinguish, where are the causes and where are the consequences?
  • Virtue is an award for itself. A man excels the virtue, if he serves never receiving an award.
  • The harm or benefit of an act depend on the combination of circumstances.
  • Things can be great or minor not only by fate's will, or contingency, but also by every person's notions.
  • Sometimes, zeal overcomes even the common sense.
  • When it concerns art, every tailor has views of his own.
  • People could not stop living together, even if they walked far away from each other.
  • If you want to be happy, be!
I'd be really happy to hear from you, and compare it with the picture in my own head.