When I get asked "What's a good first programming language to teach my [son /
daughter / other-person-with-no-programming-experience]?" my answer has been the
same for the last 5+ years: Python.
That may be unexpected, coming from
someone who often talks about non-mainstream languages, but I stand by it.
Python is good for a wide range of simple and interesting problems that
would be too much effort in C. (Seriously, a basic spellchecker can be implemented in a few lines of Python.)
There are surprisingly few sticking points where the solution is easy to see,
but there's a tricky mismatch between it and the core language features. Erlang
has a couple of biggies. Try implementing any algorithm that's most naturally
phrased in terms of in-place array updates, for example. In Python the sailing
tends to be smooth. Arrays and dictionaries and sets cover a lot of ground.
There's one caveat to using Python as an introductory programming
language: avoid the object-oriented features. You can't dodge them completely,
as fundamental data types have useful methods associated with them, and that's
okay. Just make use of what's already provided and resist talking about how to
create classes, and especially avoid talking about any notions of
object-oriented design where every little bit of data has to be wrapped up in a
class.
The shift from procedural to OO brings with it a shift from
thinking about problems and solutions to thinking about architecture.
That's easy to see just by comparing a procedural Python program with an
object-oriented one. The latter is almost always longer, full of extra interface
and indentation and annotations. The temptation is to start moving trivial bits
of code into classes and adding all these little methods and anticipating
methods that aren't needed yet but might be someday.
When you're trying
to help someone learn how to go from a problem statement to working code, the
last thing you want is to get them sidetracked by faux-engineering busywork.
Some people are going to run with those scraps of OO knowledge and build crazy
class hierarchies and end up not as focused on on what they should be learning.
Other people are going to lose interest because there's a layer of extra
nonsense that makes programming even more cumbersome.
At some point,
yes, you'll need to discuss how to create objects in Python, but resist for as
long as you can.
(November 2012 update: There's now a sequel of sorts.)
OOP Isn't a Fundamental Particle of Computing
The biggest change in programming over the last twenty-five years is that today you manipulate a set of useful, flexible data types, and twenty-five years ago you spent a disproportionately high amount of time building those data types yourself.
C and Pascal--the standard languages of the time--provided a handful of machine-oriented types: numbers, pointers, arrays, the illusion of strings, and a way of tying multiple values together into a record or structure. The emphasis was on using these rudiments as stepping stones to engineer more interesting types, such as stacks, trees, linked lists, hash tables, and resizable arrays.
In Perl or Python or Erlang, I don't think about this stuff. I use lists and strings and arrays with no concern about how many elements they contain or where the memory comes from. For almost everything else I use dictionaries, again no time spent worrying about size or details such as how hash collisions are handled.
I still need new data types, but it's more a repurposing of what's already there than crafting a custom solution. A vector of arbitrary dimension is an array. An RGB color is a three-element tuple. A polynomial is either a tuple (where each value is the coefficient and the index is the degree) or a list of {Coefficient, Degree}
tuples. It's surprising how arrays, tuples, lists, and dictionaries have eliminated much of the heavy lifting from the data structure courses I took in college. The focus when implementing a balanced binary tree is on how balanced binary trees work and not about suffering through a tangled web of pointer manipulation.
Thinking about how to arrange ready-made building blocks into something new is a more radical change than it may first appear. How those building blocks themselves come into existence is no longer the primary concern. In many programming courses and tutorials, everything is going along just fine when there's a sudden speed bump of vocabulary: objects and constructors and abstract base classes and private methods. Then in the next assignment the simple three-element tuple representing an RGB color is replaced by a class with getters and setters and multiple constructors and--most critically--a lot more code.
This is where someone desperately needs to step in and explain why this is a bad idea and the death of fun, but it rarely happens.
It's not that OOP is bad or even flawed. It's that object-oriented programming isn't the fundamental particle of computing that some people want it to be. When blindly applied to problems below an arbitrary complexity threshold, OOP can be verbose and contrived, yet there's often an aesthetic insistence on objects for everything all the way down. That's too bad, because it makes it harder to identify the cases where an object-oriented style truly results in an overall simplicity and ease of understanding.
(Consider this Part 2 of Don't Distract New Programmers with OOP.)