I disagree. The whole point of abstraction is that you don't have to care about the underlying mechanisms (although most non-trivial abstractions are leaky as Joel Spolsky has written about).
For instance, someone who writes assembly should not have to know how a transistor works. A course in introductory programming similarly should not be about how a processor works.
It's trivially easy to write perfectly valid looking Haskell programs that are abysmally slow because of how they are actually executed, and since the reason for this can't be explained at a level of abstraction of such a course, people learn to treat the language as a closed black box, while you can't really competently use any language without understanding its execution model.
Abstractions are fine, but you have to be able to switch the abstractions level when necessary and understand the bits underneath, and I think it's natural to learn starting from low abstractions level and then building up. There are many basic low level issues that surface no matter how high level is the language you are using, I am not advocating teaching assembly to beginners, but a language like C and issues like:
- Direct addressing vs. indirect adressing, aka storing a value vs storing an address, aka pointers vs. values, aka call-by-value vs call-by-reference, ... Beginning programmers brought up on very high level languages are endlessly confused by the difference between copying the value and copying the reference.
- Understanding the stack and how procedure calls work. It might then be easier to understand why tail recursion in functional languages is cool but non-tail recursion not so much.
- Basic memory management. Why data structures are such a big deal.
...
All this shows why abstraction is necessary and useful in the first place, and what it's limitations are.
It's worth noting that Djikstra (to my understanding) thought almost none of that was even contained in the field of CS. His perspective was that CS was about process and verification and proof and thus while the current implementation of computers is interesting, it didn't deserve any privilege.
So being able to prove certain nice properties about algorithms without worrying about the underlying implementation is exactly what Djikstra believed important and something that Haskell does allow you to do... even if the result is abysmal performance.
It's worth noting as well that there's a whole wonderful book [1] that provides a great glimpse of good Haskell style with the conceit that one should program the most abysmally slow correct thing first and then use algebraic theories to transform it along obviously correct paths to something highly performant.
>His perspective was that CS was about process and verification and proof and thus while the current implementation of computers is interesting, it didn't deserve any privilege.
Is that the basis for the quote about "CS isn't about computers any more than astronomy is about telescopes"?
"one should program the most abysmally slow correct thing first and then use algebraic theories to transform it along obviously correct paths to something highly performant."
Except that the syntactic variance problem [1] makes this impossible to do in theory. Unfortunately so long we use Turing-equivalent computers there will be a need for "performance programmers" and low-level programming because we cannot automate their job by formal transformations from high-level, "obviously correct" programs (theory proves so).
Well said! Understanding the machine, while useful, does not usually help you solve the problem at hand. A better approach is to teach how to create models, mental and the ones on the computer(datastructures, types etc), and show how that maps to problems at hand. Teaching how to verify models for rigor, efficiency(algorithmic), taste for elegance , and help seeing that computer science is not all that different from math, is what helps you build structures that stand the test of time.
As Brian Beckman explained, as an aside, in "Don't fear the Monad" [1], these two differing views were born in the '70s and split two programmers into two camps:
The bottom-up people, and the top-down people:
- The bottom-up people start with the hardware and only add abstractions and trade performance where necessary (fortran, c, java)
- The top-down people started with perfect abstraction/logic and reduced/removed abstraction to get access to the hardware and performance where necessary (lisp, ml, haskell)
I don't think the two camps may ever get along. Kind of like our version of Conservative and Liberal. (nor should they, in the true spirit of democracy?)
> The bottom-up people [...] only add abstractions [later]
I don't think bottom-up verses top-down are like political camps. I think it's more innate, like right-brained vs. left-brained.
I can't understand abstractions till I first understand the lower level. I could never learn algebra without first learning arithmetic. (BTW, I'm old enough to have lived through the New Math philosophy which insisted that every grade school text book start with a chapter on set theory before moving on to, say, fractions.) This doesn't mean that I can't start with a functional model, but it does mean that I start with simple functions and move up.
As someone who's strongly in the "top down" camp by observation, I'm about to refute an interpretation of what that means.
Everyone who's learned Haskell really well understands exactly that you learn monads by example, not abstraction. Abstractions need both motivation and "point samples" which describe good reasons for their particular brand of abstract behavior to be meaningful. I don't think at all that the "top down" side actually must work top down, but instead simply values the tools you get when you reach the top, i.e. strong reasoning capabilities and metaphor connections to algebra.
I really want to change the Crockford Monad Curse to instead read that "Anyone who's just learned monads is suddenly incapable of explaining them". When they first "click" you become obsessed with the abstraction as a thing instead of using it as a tool to better leverage patterns which turn up in real examples. Until you shake that by just using monads so frequently that you're damn bored you cannot properly explain them.
Same thing happens with design patterns. It probably happens with all sorts of non-obvious but powerful ideas. Hmm. Any sufficiently advanced learning is indistinguishable from madness?
I don't think anyone understands abstractions right away. Most people learn through examples, and have to recreate the abstractions themselves, even if the abstraction is explained as well as possible.
I think the main difference is purely in which abtraction people prefer to think in (imperative, OOP, functional, etc) but everyone wants to turn it into something else because to them, their model "feels" more right.
Good points, didn't know about New Math, but the failure is very insightful. I'm not sure if perhaps there should be a distinction between operational abstractions vs logical abstractions.
Scripting seems sit in a strange middle ground. Scripting languages tend to be very high level, but are often very approachable for beginners, e.g. Logo
I don't agree. There's many a programmer I know that can troubleshoot a tube radio, then go write some Lisp. Most are old school folks that grew up in the 8 bit days of solder and wirewrap.
people learn to treat the language as a closed black box, while you can't really competently use any language without understanding its execution model.
I wonder if there isn't an opportunity here. Azul's Zing VM's incremental GC basically compensates for cluelessness about how to architect and tune a GC'd language project for high performance. The first reaction might be against such cluelessness, but isn't GC that doesn't require arcane knowledge for high performance a better GC, according to the goals that GC was developed for in the first place?
The whole point of abstraction is that you don't have to care about the underlying mechanisms...
As a math person I love abstraction, I love the idealism of not wanting to worry about underlying mechanisms. As a programmer, I would note that virtually all the abstractions which programming has produced are basically failures at that level - programming languages and constructs are invariably "leaky" to the extent that on the intermediate level you wind-up having to worry about underlying mechanism after-all.
On the other hand, I think Dijkstra's wrong about one's first programming experience shaping one irrevocably. There's nothing wrong with learning pure abstraction first and then finding out it never works but there's nothing wrong with learning "bare metal" first and then learning abstraction. I think learning depends on keeping your "mental space" clean more than on what stuff you've done.
In theory, there's no difference between theory and practice--in practice, there is.
All abstractions in computers and software engineering are leaky, and you either learn enough to overcome the limitations of real machines or you learn to regret your ignorance.
Or you learn to claim that you don't actually care about the difference, because you're a theorist. That means that most of the world ignores you as irrelevant, though.
For instance, someone who writes assembly should not have to know how a transistor works. A course in introductory programming similarly should not be about how a processor works.