Monday, January 25, 2010

Reflections: A Great Programming Language, 3 lessons

As I mentioned earlier, programming enjoyableness has little to do with the programming language.  In this article, I will give 3 lessons on what makes a great programming language.

Lesson 1

First is a notion of what the goal for a great programming language is, that is, a great, not simply a good, programming language.  A great programming language knows that the programmer will want to express things whatever way they want.  Ok, that's nice.  So, what?  Well, we must discuss some things.

First, the notion of turing equivalence.  What is turing equivalence? It's a phenomenon first recognized by a man named Alan Turing.  Turing equivalence shows that if a programming language can be simulated by a special machine called a turing machine, i.e. a turing-equivalent language, then, the language can be simulated by all other turing-equivalent programming languages.  So, this puts all turing-equivalent languages on an even playing field in their abilities.  Fortunately for us, pretty much every programming language used in existence today, is a turing-equivalent language.  As an example, we could write a C++ interpreter in JavaScript (speed is irrelevant to equivalence), or vice versa.

So, what's an exercise in turing equivalence?  It is simply when we implement the facilities of one language's features in another language.  For example, if we were to implement immutable data structures in C++, or if we were to implement formal function parameters in Forth.  Such exercises in turing equivalence are considered exploitable, i.e. they provide useful, leverageable functionality in programming--this, as opposed to not generally useful functionality.  So to review, there are many useful language features, and a great programming language will let us express code the way we want i.e. using whatever language features we would want.  Therefore, the goal follows:

The goal: to optimally facillitate exploitable exercises in turing equivalence.

Lesson 2

Let's consider a lesson from practica.  Some people recognize bad code design, and in their naivete, they want to somehow force the programmer to use good code design, but this is impossible.  Bad programmers will write bad code in any language.  And how are they going to learn to write good code if they're not allowed to learn by shooting themselves in the foot?

I hear all the time about people who want to put arbitrary limits on code structure! This will never work! I hear about limiting the number of methods in a class, or disallowing global variables, or other such unnatural, arbitrary nonsense. The truth is, the programmer must learn how to design good code, and if they're a competent programmer, then whatever design choice they make is competent, and we have no business getting in their way!  So, what is the lesson learned?

Don't design a PL for bad programmers.

A language designed for bad programmers is a burden on competent programmers.  Such a language will create a vicious cycle, always attracting bad programmers to the language.  The perfect programmer knows what they want. A good programmer can figure out what they want. We should always assume a competent (perfect or good) programmer, because programmers are able to improve themselves to become at least good!

Lesson 3

After establishing lesson 2, that we can focus on competent programmers, we soon realize the third lesson:

A great programming language simply stays out of the competent programmer's way.

So now the question becomes: how do we stay out of the programmer's way?  There are only 2 ways to stay out of the programmer's way:

Method #1: By already providing the programmer's desired language features.

This removes implementation detours out of the programmer's way. This would be nice, but it will never realistically always happen for all programmers, for all their uses of the language.

The second method is not such a direct approach, but we will see it's better:

Method #2: By providing the capabilities to bootstrap the features desired by the programmer.

This, while obviously not as convenient as the first option (some implementation detours are required at times), is much more scalable: the language is guaranteed to never absolutely get in the way.

The perfect programmer knows what they want. A good programmer can figure out what they want. The first method never helped anyone get exactly what they want.  A great programming language will never tie the hands of a perfect or good programmer, so method 2 is the only possibility.

Friday, January 22, 2010

The Great Programmer

It seems like all programmers want to find a perfect language for them. Now, don't get me wrong -- I want a perfect language as well, but we all know there never will be such a thing, and the truth is, programming is not about having a perfect programming language that does stuff for you just by telling it what to do in plain english. If such were the case, we wouldn't need programmers.

So now what? Are we forever doomed to hate what we do? Well, actually there is hope: the key to enjoyable programming is in having a great programming experience. Experience? What does that mean? It means that we feel like we've solved problems, invested in future productivity, and/or made things better in a session of programming, while not stalling our progress towards the development of the software we are programming.

As we can see, this hasn't alot to do with the language we're using. And this brings me to what a great programmer is. You know the old saying that a bad programmer can write bad code in any language? Well, the great programmer is one at the opposite end of that spectrum:

A great programmer produces great code in any language.

Yep, that's right. A great programmer is one who has gotten so good at coding, that they have risen above the mire, the style that their everyday language nudges them towards, and they have found those good architectural and semantic patterns that make for great code, and then they can write great code in any language!

How do you recognize a great programmer?
  • They tend to design reusable libraries, routines, and code, in general. Instead of writing 1,000-line algorithms, they develop a 1,200-line reusable, well-designed library, and simply reuse it when they need it again.
  • They don't need to rely on comments: they read the code itself.
  • They limit the amount of architectural aerobatics! Instead of spending 3 months creating a scripting programming language in the programming language, they spend a week creating an object-oriented system that is not much harder to deal with than a scripting language.
  • They limit complexity! Instead of coding a perfect routine that takes 3 hours to write and 2 days to debug, they write a very simple, good-enough routine that takes 5 minutes to write, and doesn't have bugs.
  • They seem to have ingrained habits of how to write good code and make good design choices. They've been here before, and they know that design X will easily solve problem Y.
  • Their work is excellent in design. They know that abstraction means well-designed, flexible libraries.
  • They optimize only 1% of the time, but when they do optimize, those optimizations produce durastic improvement.
  • They code defensively. They attempt to ingrain air-tight error prevention into their libraries, so that nobody can use them incorrectly without knowing about it. They seek to catch errors on program start-up where possible. In addition, they exploit the whatever type checker is available to prevent errors at compile-time when possible.
  • They test the functionality immediately, to make sure it works. Coding a feature means both writing it, and ensuring that it works correctly.
So, in summary, a great programmer goes for reusability, streamlining, simple, good-enough, anti-optimization, defensive coding, immediate testing.