
You can't dig upwards - plg
http://www.evanmiller.org/you-cant-dig-upwards.html
======
tptacek
10+ years of professional C programming experience --- which I wouldn't trade
for anything --- do not make me understand monads any better. Nor do I think
that looking at Haskell as a bunch of stuff implemented in C is going to be an
especially rewarding approach to learning Haskell.

At the risk of getting myself shivved:

The mistake here is that Python is a bad example. There isn't much beyond
table stakes to learn, conceptually, from Python.

There _are_ concepts that C is uniquely good at engaging: the memory
hierarchy, object lifecycles and memory allocation, locality, implementation
of indirection.

And there _are_ concepts that Lisp is uniquely good at engaging.
Metaprogramming, recursion, the bag of ideas involving closures, lambdas, and
their relationship with encapsulation and abstraction.

But the most popular languages --- Python, Ruby, Java, PHP --- don't offer
much more than productivity.

I think you need C. I think you need a serious high level language --- a Lisp,
an ML, Haskell --- as much as you need C. And then I think you need a Python
or a Ruby, but if you have the previous 2, you get Python and Ruby for free
anyways.

~~~
lucio
I have the same problems with haskell. My actual level of ignorance is:
(anyone having traveled the path between imperative and functional, please
correct me)

\- 1st problem: Functional Programming is not "programming", since in "pure"
functions _you do not have algorithms_ and "programming" seems to be defined
as making "algorithms".

\- I believe it should be called "functional composition" instead of
"functional programming".

\- 2nd: Monads. "Monads" seems to be the way to introduce "impurity" into
Haskell, so, if you need an algorithm, you enter the "monad" space.

~~~
m0nastic
I feel like you're conflating "purity" with algorithms.

Just to clarify, in Haskell there is no issue with algorithms being contained
inside functions.

Here's a naive implementation of quicksort:

    
    
      qsort [] = []
      qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
                     where
                     smaller = [a | a <- xs, a <= x]
                     larger = [b | b <- xs, b > x]
    

Nothing about this algorithm requires side-effects, it doesn't mutate any
state, it doesn't make use of counters. You can implement it anywhere inside
of a Haskell program.

People may charitably point out that this is a pretty bad naive
implementation, because it doesn't just mutate values in the list up and down,
which is inefficient. People uncharitably will take issue with even calling it
quicksort, as some people believe that the property of just mutating values in
the list is an integral part of it being quicksort. I'm not smart enough to
have an opinion about whether or not it's a "true" quicksort.

But the point is that you can absolutely have algorithms in Haskell that are
not restricted to only run in a Monad.

Monads exist in Haskell as a way to reason about side-effects. Whether or not
an algorithm (or any function) needs to be executed inside a monad is a
function of its use of side-effects, it's not fundamental.

~~~
lucio
Thanks. As I said in my comment I know my ignorance and I'm looking for
enlightenment (my confession didn't stop the downvotes though)

I do not "see" any clear algorithm in the code (no step-by-step procedure). I
see recursion only.

¿Can you explain in words what this code is doing?

¿Can you implement this without using recursion?

~~~
m0nastic
Ok, here let's step through what this function is doing with a simple example
(sort a list that looks like this: [3, 5, 1, 4, 2]:

    
    
      qsort [] = []
    

This means that if you hand the qsort function an empty list, you get an empty
list ([] is the empty list in Haskell).

    
    
        qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
    

Ok, so this means that we are making a list out of three parts: (qsort
smaller, x, and qsort larger). The '++' operator is just concatenation.

    
    
                      where
                      smaller = [a | a <- xs, a <= x]
                      larger = [b | b <- xs, b > x]
    

Here we're defining what "smaller" and "larger" are. They're list
comprehensions. "Smaller" returns a list that is made up of pulling out all
the numbers that are less than or equal to x. "Larger" pulls out all the
numbers that are greater than x. Then it does it again.

Using that example list above ([3, 5, 1, 4, 2]), here's if we did it by hand:

    
    
      qsort [3, 5, 1, 4, 2]
       = {applying qsort}
      qsort [1, 2] ++ [3] ++ [5, 4]
       = {applying qsort}
      (qsort [] ++ [1] ++ qsort [2]) ++ [3] ++ (qsort [4] ++ [5] ++ qsort[])
       = {applying qsort}
      ([] ++ [1] ++ [2]) ++ [3] ++ ([4] ++ [5] ++ [])
       = {applying ++}
      [1,2] ++ [3] ++ [4,5]
       = {applying ++}
      [1,2,3,4,5]
    

That "step-by-step procedure" is an algorithm.

Is it that there are no loops that you don't see how it's an algorithm? It is
definitely true that some algorithms are very obvious to implement using loops
(quicksort is actually one such algorithm), and can in fact be a pain in the
neck to implement in some functional languages.

~~~
kazagistar
(You made a small mistake qsort [1, 2] ++ [3] ++ [5, 4] should be qsort [1, 2]
++ [3] ++ qsort [5, 4])

Also, quicksort is a pretty bad example of an pure algorithm, since it loses a
lot of its performance benefits when not done in place.

~~~
m0nastic
Ah, you're correct. (It's too late for me to fix my comment).

I went over the issues with this version of quicksort in the original comment,
but I still think it was a good example because most people are familiar with
quicksort, and I was trying to address the original posters claim that
algorithms in Haskell could only be used in Monads.

Also, quicksort is one of the few I can do from memory.

------
GuiA
We can argue all we want about these things, and try to reason as logically as
possible, and write entire essays on the topic - ultimately, if we want
meaningful discourse, we need data on programmer skill vs first language
learned.

My first serious language was z80 assembly, followed by C, and I have a voice
in my head that, much like the author, says "of course someone who's learned
assembly/C first will be a better programmer". But we need to fight back those
instincts, because they are not based on data. Sure, they seem logical, and we
can make eloquent parallels with stick shift driving, and we can all remember
that one programmer who started as a Python programmer and totally sucked, so
of course learning Python first doesn't make great programmers - but at the
end of the day, we need data. It's all pseudo-science otherwise, and it's not
any better reasoning than pop psychology.

It's kind of ironic how hackers, usually extremely data driven people, tend to
fall into these fallacies just as easily as others as soon as it stops
pertaining to engineering. We wouldn't want to debug a program or assess its
performance without any hard evidence, so why do we do that when it comes to
thinking about other things? Some of pg's essays come to mind, e.g. the one
about smart kids being unpopular and miserable in high school [0]. He might be
right, he might be wrong - I don't know. But I do know that building an entire
social theory just based on your hunches and own subjective experience is not
scientific. It's very tempting to want to do so - we're very logical people,
and in many cases in our jobs we can derive objective truth just from the
power of deductive reasoning - but we need to fight those instincts when it
comes to reasoning about fuzzier systems, like society or other human beings.

The idea exposed by the author is mentally pleasing - many of us are tempted
to agree because it feels right, and the author's reasoning seems right. But
there are no correlations between mentally pleasing, elegant ideas and their
correctness. It was once mentally pleasing to think that the Sun went around
the Earth, or that electricity was a fluid.

EDIT: as for the claim that stick shifts make for better drivers, then why do
countries with 99.9% of cars having manual transmissions (e.g. France) have
comparable, or worse, accident rates per kilometer driven per person compared
to the US? The fact that this is the case should at least cause one to pause
rather than blindly assert "stick shifts make better drivers".

[0]
[http://www.paulgraham.com/nerds.html](http://www.paulgraham.com/nerds.html)

~~~
lightcatcher
I think the author's argument can be split into two generalizations:

(1) people who know C are better programmers

(2) people who learn Python first never learn C

I think (1) is generally true. I would be interested to hear someone explain
why they think having low-level programming knowledge makes you a worse
programmer than one without low-level knowledge.

I disagree with (2). I took a high school class on Java, they spent several
years programming in Python and becoming relatively proficient. As the author
described, I eventually hit the "hard dirt" where I started to having to worry
about the OS, memory, etc. I then learned C and C++, operating system
internals, high performance computing, GPU computing, etc and all of the low-
level ideas that the author thinks that those who learn Python will never
know.

However, both statements (1) and (2) are generalizations, and I've only shown
that I violate (2). I believe (2) may actually hold for many people, and I
agree with the author's idea that if people learned C first we would have
fewer programmers but perhaps also better programmers. Of course, "better
programmers" is very hard to define and depends a ton of the domain of tasks
over which you're evaluating the programmer.

~~~
sillysaurus3
_(1) people who know C are better programmers. I think (1) is generally true.
I would be interested to hear someone explain why they think having low-level
programming knowledge makes you a worse programmer than one without low-level
knowledge._

This isn't related to the argument per se, but it's interesting to observe
that website ninjas are making >$150k, whereas most low-level ninjas are
making less. The reason is because most people want websites, and all of my
SIMD knowledge becomes irrelevant in that case.

"Better programmer" is very hard to quantify. Whereas "makes more money, on
average" is a solid metric.

~~~
lightcatcher
I agree that it's hard to find low-level work with equivalent pay to web
development/frontend/etc. I think this is a reason that the "learn to code"
movement focuses on Javascript and Python rather than C.

From my limited experience, big companies (Google, Facebook, etc) are a good
place to do low-level programming with good pay. Additionally, a lot of the
programming in the financial industry (particularly high frequency trading) is
very low-level work, and the pay is significantly better than the standard
"Bay area web company".

------
arh68
Sorry, this really grinds my gears ;)

> _we know what the Overdrive gear in automatic-transmission cars means, and
> why you use it to pass a truck on the highway_

That is not what overdrive is [1]. Overdrive is the very high gear that lets
you nearly idle at highway speeds. Stepping on the gas would strain the
engine, until the automatic _downshifts_ , which is what the author seems to
describe.

[1]
[http://www.wikiwand.com/en/Overdrive_(mechanics)](http://www.wikiwand.com/en/Overdrive_\(mechanics\))

EDIT: it seems strange, coming from a miata driver, since those gearboxes
definitely lack an overdrive, requiring high RPMs at highway speeds (like a
racing transmission, tight ratios)

~~~
kec
Your definition of overdrive is slightly incorrect. Overdrive simply means
that the overall ratio between the input to output shafts of a transmission is
less than 1.0 (meaning the wheels of the car are spinning faster than the
engine).

By this definition the Miata _does_ have an overdrive as most generations have
a final ratio of around 0.85 in top gear.

~~~
sokoloff
Your definition is correct, but your conclusion is incorrect.

The wheels of the car are not spinning faster than the engine (in the
overwhelming majority of cars) because there is still a final drive ratio
between the output shaft of the transmission and the wheels. That is rarely
taller than 2.50:1 and often greater than 3:1.

Look at the revs per mile in a tire's specification, drive that car at 60mph,
and see that the engine RPM is higher than the revs per mile figure, even in
the tallest overdrive gear that you have.

------
aroberge
I think we do a disservice in introductory physics when we have things like
massless ropes that do not stretch, massles pulleys and neglect air
resistance. If we teach them those concepts first, how are they going to be
able to truly grasp the effect of deformable solids; they will hit a wall and
have no interest in learning about the Navier-Stokes equation.

And don't talk to me about teaching the ideal gas law without having taught
quantum statistical mechanics and quantum mechanics so that students can truly
understand all the approximations involved in deriving the ideal gas law from
first principles.

Furthermore, don't even think of teaching people programming in C until you've
taught electro-magnetism, used it together with statistical mechanics as
foundation to understand condensed matter theory so that they can understand
what those NPN and PNP mean, and what transistors are built.

~~~
learnstats2
> I think we do a disservice in introductory physics when we have things like
> massless ropes that do not stretch, massles pulleys and neglect air
> resistance.

This might actually be a good point.

A lot of students struggle with these ideas because they can't match the naive
model to reality and so it's a struggle to understand the purpose of it.

Then, a lot more students struggle to get past this type of naive model
because they understood the naive model was the point of what they were
learning and it turns out - not really close to the end goal.

Sure, most anyone can learn the naive model of mechanics well enough to pass
high school physics. But, it takes an excellent teacher and/or an excellent
student to get past those two cognitive obstacles and progress any further. I
recall physics to have a shortage of students, shortage of teachers and
shortage of practitioners, a deficit that's getting worse.

I understand you are trying to make an ironic point but it's not obvious that
it should be ironic. Is a naive model the best way to teach people mechanics?
It works for some people but it's at least arguable.

~~~
aroberge
If you have stretchable ropes, massive pulleys and air resistance, you'll need
to be able to solve some complex differential equations which, in the North-
American context, would not be taught until 2nd year university. Yet, the
simple concepts of conservation of energy and momentum can be taught using
simple algebra at the high school level with these idealized systems.

As for the deficit of practioners ... there are at least 10 times as many
people getting a Ph.D. in Physics in Canada and the U.S. as there are
positions ... so, no, there is no real shortage. (There is a shortage of
qualified high school teachers though.)

~~~
learnstats2
You absolutely don't need to solve complex differential equations for this
purpose, unless you want a closed form. Let's be more imaginative: Numerical
analysis of the same model would be fine for high school level and is likely
to give a great and relevant insight into mechanics.

Why is the closed form considered a prerequisite for a numerical
approximation? Just because we ultimately end up at the latter, doesn't mean
it should be taught in that order.

I may be mistaken about the practitioners but I doubt many Physics PhDs are
short of work. It certainly seems like employers want more of them.

~~~
aroberge
Whether you find a closed form solution (and, for most realistic systems you
will not) or you want to solve it numerically ... you still need to write a
set of differential equations. Before you learn about differential equations,
you need to have done some basic calculus. You can not teach high school
students what differential equations are, how they describe mechanical
systems, and how to solve them numerically. However, using the math they know,
you can teach them to find solutions to idealized systems that illustrate
conservation of energy and momentum which are fundamental physics concepts.

~~~
learnstats2
OK. I accept much of what you say.

But it's perhaps not a coincidence that calculus and classical mechanics were
invented at the same time in history - indeed, both were advanced
significantly by the same person. Classical mechanics and calculus are two
sides of the same coin.

Fundamental Theorem of Calculus: <=1670 Principia Mathematica: >=1687

So which order do you prefer to teach them?

In truth, HS math students _do_ learn about differential equations, and easily
have the capacity to apply them numerically. Some historical context and a
little creativity in teaching could greatly help the understanding of both and
benefit many students.

------
smacktoward
There is an important way in which writing C (or really any language where you
allocate memory manually) is not like driving a stick.

The thing about driving a stick shift is, when you make a mistake, you _know
it immediately:_ you hear gears grinding, or see the tach leap up because you
downshifted too far, or start rolling backwards downhill. The car gives you
_instant feedback_ on your performance as a driver. You learn from this
feedback and become a better driver. You know you're a better driver because
the bad feedback doesn't happen anymore.

Making a mistake when programming in a language where you manage memory
manually is frequently not like this; you don't get the instant feedback when
you screw up. The code compiles, the binary runs, everything looks fine. It's
only days or weeks or months or years later that you realize that, while it
_looked_ fine, it has actually been slowly leaking memory, or in certain
circumstances can lead to a buffer overflow, or so forth. Maybe you _never_
realize it. So, without the prod of that instant feedback, you _don 't_ become
a better programmer. You just keep writing crappy, broken C forever.

------
aidenn0
Reading this make me think Evan isn't particularly familiar with Lisp. No lisp
developer I've ever worked with is unaware of what a cache line is, nor the
importance of it for optimzation.

Optimizing a lisp program is very similar to optimizing a C program: a
profiler will be used, and the disassembly will be inspected. The main
difference is that the pattern of allocations matters a bit more, as there is
a garbage collector consuming CPU time along with the mutator (Note that in
some cases heap allocation can be just as cheap as stack allocation, as a
generational garbage collector that uses Cheney's Algorithm for the nursery
will collect short lived objects at essentially no cost).

~~~
skybrian
I don't know about that. We didn't learn anything about cache lines while
learning Scheme using _The Structure and Interpretation of Computer Programs_.
Maybe that comes later?

~~~
aidenn0
1) My primary experience is with Common Lisp, not Scheme [1]

2) In any event, I wouldn't call someone whose total lisp experience is a SICP
class a "lisp developer" and my introductory class in C didn't mention a cache
line either (nor did we ever look at disassembly).

[1]: A quirk of the lisp community likely surprising to those who aren't
involved is that whether or not Scheme is Lisp is debated. For the least
flame-ish response by a prominent lisper, see
[https://groups.google.com/d/msg/comp.lang.lisp/Bj8Hx6mZEYI/6...](https://groups.google.com/d/msg/comp.lang.lisp/Bj8Hx6mZEYI/6AWmNEwQR5YJ)

------
jerf
It seems to me this is a better argument about why we should be teaching
Python and Assembly than Python and C. C is ever-increasingly a poor
abstraction of a modern system anyhow, and thus ever-increasingly a poor way
to learn what's _really_ going on under the hood. (It's been a slow process,
spanning decades, but it has been a one-way ratchet, and there's been quite a
few decades now.) People really need to stop speaking as if C is a good
representation of the underlying system.

~~~
qznc
> C is ever-increasingly a poor abstraction of a modern system anyhow

I have heard this statement quite often, but never really understood it. What
modern features does C support so poorly? Multicore? GPUs? Distributed? SIMD?
Cache Layers?

~~~
aidenn0
It sounds like you nailed it on the head to me.

~~~
jerf
Indeed. I would also add GPU programming, and also a number of features of
modern processors such as hardware STM and some other such things tend to be
things that C supports only poorly or as an afterthought, despite the fact
Intel generally has to keep C compatibility in mind as a design criterion in
the first place, an advantage shared by virtually no other language. (Perhaps
C++.) It has also long had the "aliasing" problem which is the reason Fortran
kept its position as a math language for so long.

Of course in each case, C has been extended so as to do "something" with those
features. (Excepting GPU programming, which is just _such_ a different
abstraction that C just stops working entirely.) But they are extensions
bolted on after the fact, to varying degrees, not something well-integrated
into the core language and its core runtime, which remains an abstraction of
the 1970s computers from which it emerges.

Which is, all things considered, not _that_ great a criticism, seeing as how
few modern languages are terribly optimized for these cases either. The very
cutting edge of general-purpose languages today are arguably focused for
hardware now merely 10 years old. It doesn't mean I think C is "bad" (... or,
at least these reasons aren't related to my real objections). It just means
that C, while a great guide to how computers worked 40 years ago, is not all
that great for the much richer and more interesting machines of today.

I suppose one can make a solid argument for teaching the 1970s model as a
valid simplification. Still, I'd prefer that professionals see it as such,
rather than have the unexamined idea that C is truly "how computers work".

~~~
aidenn0
> Indeed. I would also add GPU programming, and also a number of features of
> modern processors such as hardware STM and some other such things tend to be
> things that C supports only poorly or as an afterthought, despite the fact
> Intel generally has to keep C compatibility in mind as a design criterion in
> the first place, an advantage shared by virtually no other language.
> (Perhaps C++.) It has also long had the "aliasing" problem which is the
> reason Fortran kept its position as a math language for so long.

There is a lot wrong with this paragraph.

1) The comment I replied to mentioned CPUs

2) the "S" in STM is software, "hardware STM" is a misnomer, it is rather
"Transactional Memory" and isn't a particularly modern feature. Hardware
designers have dabbled in it for decades, and I don't think it's particularly
more popular today than previously

3) C89 already had fairly strict aliasing rules that systems programmers hate
so much that every modern compiler has a flag to disable it. Furthermore C99
added the restrict keyword which eliminates this problem (though yes, the
motivation for not including "restrict" in C89 was braindead.

------
antirez
I've a different experience. I started with C as my main language. For years I
touched C very rarely, and focused on Scheme, FORTH, SmallTalk, Tcl, and other
non conventional languages. What I found is that my knowledge of C did not
prepared me to understand the higher order abstractions, like call-cc, or the
pure nature of object oriented programming (that IMHO you don't capture
learning C++, but only with SELF/SmallTalk). Later after learning call-cc or
functional programming I could make sense of that language features in a C
implementation of the interpreter maybe, but, it is a separated process:

\- If you stay low level you don't understand high level abstractions. And
this will weight as a negative thing in your C code as well.

\- If you stay high level you don't understand how it actually works at the
machine level.

You freaking need both. The problem of certain languages is that they are not
like C, but they are also not like SmallTalk or Lisp or FORTH. They just
resemble a more dynamic version of C, so you don't really gasp fully any of
those levels.

However if you pick the languages to learn wisely, the magical thing is that
once you take a trip in the higher order abstractions, you return back to C
and code in a different way.

------
dragontamer
Except all the C Programmers I know are so deathly afraid of C++ that they
don't know how shared_ptr<> works or how to do template metaprogramming.

As a result, C Programmers grow stuck in their own path, unable to advance.
The _real_ issue is that most programmers stop learning eventually.

True, getting "stuck" at the Python level and refusing to learn about the
inner C workings of OSes can stunt your growth.

But similarly, getting "stuck" at the C level and refusing to learn how
powerful abstractions (ie: Metaprogramming... like Hygenic Macros from Scheme
/ Templates from C++, or OOP / Design Patterns) is just as much of an issue.

And believe me, if you work with "typical" C Programmers, you'll know that
they are almost assuredly "stuck" in their ways of programming.

The typical programmer gets stuck. The goal of any programmer who wishes to
get better at his craft is to _always_ progress towards the unknown. Try to
understand programming at levels beyond whatever your current work environment
is... it is a big world out there.

Never get stuck. It doesn't matter if you started in C, Python, Visual Basic
or anything else. Just keep practicing and try to understand everyone's
perspective.

------
ggreer
I preface this comment by mentioning that I come from a similar background as
the author. Although I played with Logo and QBasic in elementary school, the
first language I really learned was C. Likewise, my car in high school was a
stick shift. The author makes some mistakes about transmissions (overdrive
does not do what he thinks), but I'll ignore them since I want to focus on
programming.

While C and manual transmissions are fun and rewarding for enthusiasts, I
don't think teaching them first would benefit the programming and driving
communities.

Learning to program is difficult and frustrating. Learning C first, more-so. I
should know. I took data structures (taught in C) when I was 13. I'd been
working on a homework assignment for a couple of days, and couldn't get it to
behave as desired. I'd inspected every line of code countless times. I'd
saturated the code with printf()s, but I still couldn't figure out why it
wasn't working. It was as if a condition was always true when it shouldn't be.
I was almost in tears when I asked my dad for help. He immediately saw an if
statement with an = instead of a ==. Back then, gcc didn't default to warning
about assignment in conditions. I was angry at having such a small error cause
me so much grief. After having that experience, any reasonable person would
say, "This is crazy. I'm going to do something else with my life." Many of my
classmates did say that. About 50% of students failed. I imagine more would
have persisted if the language was Python.

It's a similar story for learning to drive stick, though the stakes are
higher. Learning to drive is difficult and dangerous. Adding the complication
of shifting makes it even more difficult and dangerous. In my first week, I
managed to roll backwards into another car at a stoplight. I had some very
close calls due to my mind being occupied by shifting. In hindsight, I'm lucky
nobody was injured due to my inexperience.

Teaching C and manual transmissions first will likely get you better
programmers and drivers, but you will have significantly fewer of both. The
author implicitly makes this trade-off by not recommending that everyone first
learn to use assembly and motorcycles. After all, both are more difficult than
(and will teach you skills that are useful for) everyday programming/driving.

~~~
mariodiana
> I was angry at having such a small error cause me so much grief. After
> having that experience, any reasonable person would say, "This is crazy. I'm
> going to do something else with my life." Many of my classmates did say
> that.

But when does this grief stop?

A few months back I had the task of implementing the receiving end of what was
basically a single-sign-on scheme. I had to use SAML. If you don't know that
that is, consider yourself lucky -- or, just google "I hate SAML." That was
one of the early hits I found when I first began researching the subject.

In the end, the implementation wasn't all that difficult. But the
documentation was another story. The overview documents were buzzword bingo;
the spec was an exercise in tedium; the libraries were poorly documented; the
mailing lists were horrible. The complaining of people on the Internet about
all this was demoralizing. When I finally figured it out and got it working, I
felt like the payoff was poor compensation. It was just too much frustration
for what I accomplished.

Programming has its share of this kind of frustration. (So does systems
administration.) Just taking your average manpage as an example, we work in an
industry where people have no capacity to explain anything to someone who
doesn't already understand. A programmer needs inquisitiveness, yes; but that
inquisitiveness needs to be steeled by an unreasonable amount of grit.

If programming were full of nothing but the fun of for-loops, it would be for
everyone. As it is though, I perfectly understand why many otherwise smart and
capable people choose to do something else.

I love Python; but maybe using it as an introductory language -- in college,
no less -- is a little bit like luring children with candy.

~~~
ggreer
> But when does this grief stop?

It doesn't, but we get used to it. I take the same view as Douglas Crockford:

 _I think there has to be something seriously wrong with you in order to do
this work. A normal person, once they’ve looked into the abyss, will say, “I’m
done. This is stupid. I’m going to do something else.” But not us, ‘cause
there’s something really wrong with us._ [1]

I'm pretty sure this "brokenness" can be trained, but tossing someone in the
deep end is not the way to do it.

1\. From Programming Style & Your Brain:
[http://www.youtube.com/watch?v=taaEzHI9xyY#t=26m50s](http://www.youtube.com/watch?v=taaEzHI9xyY#t=26m50s)

------
kutenai
I've been programming for more than 3 decades. I actually learned Basic first,
then DBase, then ASM. I didn't learn C until I'd been programming for about 8
hears. I then learned C++, and Perl, as well as SQL, etc..

I don't feel that learning C would have been the best choice to start with.
And the stick shift analogy really not a very good one.

Nobody ever needs to learn to use a stick shift if they don't want to drive a
stick. End of story. If you don't need C, you don't need it.

C is great for embedded programming, although I'd prefer at least a limited
version of C++ myself. Pure C? I personally don't fine a lot of uses for it.

If your a web developer, or any one of a large number of modern programming
areas, you may well NEVER need C or C++. To imply that all programmers need C,
is, kind of antiquated IMO.

------
bstpierre
Are universities using single-language curricula now? I hear people talking
about "python schools" or "java schools". That seems like a mistake. I don't
have any hard data to back it up, but it seems like students would end up
better off learning a small number of languages across multiple paradigms
while they are in school.

e.g. Intro CS 1/2 in python, followed by a computer architecture course that
introduces an assembly/machine language (you could write an
assembler/disassembler in python), possibly followed by C (since pointers will
make sense now), and finally something different like lisp or ML family.

------
drv
If anything, if we're serious about teaching how practical computers actually
work today, I think that an assembly language should be the first language
taught to computer science students. C still hides too many details and
imposes design decisions that make much more sense after seeing the chaos of
unstructured programming.

Additionally, I don't know how to drive a manual transmission car, but surely
you would use a lower gear, not overdrive, to pass a truck on the highway.

~~~
tptacek
Knuth would agree with this, right?

~~~
cracoucax
Probably, but only if the target architecture is a 6 bit processor which
doesn't exist.

~~~
tptacek
Holy enormous serendipity thread moment. This won't make any sense to you, but
I'm saving a link to it, and mark my words, it'll make sense in a couple
months.

------
dragonwriter
You actually can dig upwards. I learned to drive automatic first -- and still
learned to drive stick (as is true of quite a lot of people I know, many of
whom came to prefer stick.) And I learned BASIC before assembler
(BASIC:Assembler is weirdly similar to Python:C) -- and still learned the
latter.

And, in any case, structured programming languages in general (including C)
are pretty poor for developing "a proper mental model of how computers work".
You'd be better with assembly -- or even BASIC, which despite its more
beginner-friendly syntax is structurally very simply to assembly.

OTOH, Python offers a lot less distraction than something like C when learning
CS concepts (much of my early formal CS instruction was in C, and I've seen
people more recently go through similar things in Python).

Also, I think, contrary to the claims in the article, that there are far
_more_ people now than at any time in the past capable of writing "extremely
good software" of the type the article discusses (that is, extremely well-
tuned on a low level), and that lots of them _are_ writing such software,
often the tools that make it unnecessary for most programmers on most tasks to
_focus_ on low-level performance optimization in order to have software that
is fit for its purpose.

Now, its true, that the programmers capable of doing that kind of low-level
tuning may be a smaller _percentage_ of all programmers than a generation ago,
when C was considered a fairly _high_ level language and not a fairly _low_
level language (when that ratio was lower than it was a generation earlier
than that.) But, so what?

~~~
mark-r
Yes you _can_ dig upwards, many people have. I think the point was that an
increasingly small number of people will find it in their own interest to do
so. And they will be poorer for it.

I'm told that the number of manual transmission cars is declining year over
year as well.

~~~
dragonwriter
I think less people (proportionately to the total number who enter the field
at all) will find it useful to dig upwards over time not because its hard to
do -- which doesn't really change over time -- but because the better the high
level tools are the less benefit there is from going low level.

Same with manual transmission, which offers less benefit over better
automatics (and even less over CVTs.)

------
Marazan
False dicotomy. You can learn both at the same time.

Also makes the classic smug C programmer logical fallacy that assumes that C
is the perfect way tp learn how a computer really works rather than Assembler
or raw machime code.

------
roarktoohey
For the brilliant the starting point won't matter. For the average getting to
results fast will.

Too many people have mental blocks of "programming is too hard". That is,
until they see 2+2 or print 'hello world' and it just works.

Far more average people out there than brilliant, let's focus on those and let
the geniuses fend for themselves, i'm sure they'll do fine.

------
austinz
I think maybe the argument should be that C 'makes explicit' certain
fundamental concepts - pointers and pointer dereferencing, explicitly choosing
between allocating memory on the stack versus the heap, the idea that things
like arrays, strings, objects, trees, etc can be built out of very basic
abstractions. These are concepts that are important to know in order to really
have a good idea of what is going on when you program in a higher-level
language, even if you never actually do work that requires C's error-prone
string handling or manual pointer manipulation.

I don't know if C is necessarily the _best_ language to familiarize users with
these concepts. Rust makes users grapple with the implications of explicit
memory management but does so in a way that is far less dangerous at runtime
than C (at least in many cases). So maybe figuring out how to reason about
pointers and lifecycles and ownership in an explicit way in order to get a
Rust program to compile is better than messing around with code that segfaults
and does weird things until it happens to start working properly.

But I still think there is some value to hands-on experimentation. Concepts
like "primitives are passed by value, but objects are passed by reference" in
a higher-level language might seem like arcane trivia to a beginning
programmer, but someone who has played around with the same idea in C (or
another language where this sort of thing is reified) might be better
positioned to understand the underlying rationale.

------
calinet6
This is just really good, really well explained, and full of truth.

No further comment. There is absolutely nothing bad about learning the inner
workings of your machines. Go even lower than C if you can -- learn assembly,
learn about logic gates and memory, learn some electrical engineering, and the
physics underpinning it. It just makes the magic more amazing.

And you may think it is a waste of time, but you have to have a little bit of
faith that it isn't. Because you will be a different kind of programmer once
you cross that threshold.

------
morgante
I do think there is some value in learning C (and Assembly), but it should
definitely _not_ be the first language you use.

How most colleges seem to work these days is that your first 2 years or so are
nearly all Java or Python. That makes total sense: at this point, you're
learning conceptual techniques (algorithms, data structures, etc.).

C is introduced through an Operating Systems course, and there its value is
absolutely apparent. The big problem though is that C and OS are _hard_.
Without the compulsion of a grade, I almost certainly would have given up
dozens of times, even though looking back I truly appreciate the course. While
I've never touched C in my professional career, that one course was enough for
me to reason about the impact of OS choices on my programs. Unfortunately I
don't think 99% of "self-taught programmers" will ever bother to struggle
through OS because it's very hard and there's no immediately obvious gain.

~~~
tptacek
Why shouldn't C be the first language people learn? It was the first language
I learned, by myself, and I promise you that I'm not a particularly smart
programmer. If there's value in understand how a program actually executes on
a computer, why not teach that at the same time as "loops and functions"?

~~~
Klinky
If we're talking about general educational courses, then accessibility is
important. Dynamic object-oriented languages can offer immediate feedback and
gratification which can feed into keeping students engaged and motivated.
There is also something to be said about the low-level features of C not being
as important today as they once were. Many people make their living in
languages that don't involve low-level access at all, and may even go their
entire lives relying on a garbage collector. C offers freedom, control,
understanding, but those features held as virtues by some feel more like
busywork and bookkeeping to others.

~~~
tptacek
Are we talking about vocational training or computer science? If the former,
Python or Ruby is probably all you'll need.

~~~
Klinky
I think we're talking about first languages, which many people will be
introduced to in either college, advanced placement courses, or standard high
school classes. The USA has generally poor vocational training opportunities,
especially for coding. If we're talking about learning a language on your own,
there is no way to force anyone to learn the specific language you want them
too, so going down that rabbit hole seems moot.

Some people will have the drive and opportunity to learn to code in C, just
like the author had his father's car with a manual transmission and parents
who forced him to learn how to use it. However, not everyone is coming from
the same background.

Joe Nerd Jr could probably start with C. Joe Public Jr probably needs an
easier introduction. We don't have med students immediately dissecting
cadavers, nor do we have basic math classes that start you off with calculus.
While some people will feel starting with these advanced classes makes you
understand the subject better, many people will bomb out completely and become
alienated with the topic. Perhaps some like the concept of survival of the
fittest, but it may not be beneficial to society in the long run.

Most computer science students end up in the programming field. They do not
stay in academia perpetually. Usually if someone wants to become a pro coder,
they're told to go get a BS in CS. We could debate what a True Computer
Scientist is, but in the end most people are there because they like
technology and want a well paying job. Some just won't be hardcore enough for
some people's liking.

~~~
tptacek
Wait, we _do_ have med students dissect cadavers immediately, right? Anatomy
lab is a year 1 course.

~~~
aroberge
I don't know about the country where you live but in Canada, student take some
anatomy and physiology classes first before they are admitted into a medical
program, and they certainly do not dissect human cadavers in those anatomy
courses.

------
rsync
"We know what those mysterious 1 and 2 labels below Drive represent, and why
you engage their services when coasting downhill."

I've never understood this.

That is, I understand completely how people choose to shift into lower gear to
lessen their speed gains due to descent down the hill ... I get that.

But what I don't understand is why ? Your brakes are replaceable
_consumables_. Your transmission is an integral mechanical part of the car,
the failure of which (in most cases) is synonymous with the failure of the
entire car.

So why, given the choice, would you choose to divert wear from the brakes
(where it belongs) to the transmission (where you don't want it) ?

Is it just assumed that you won't have decent brakes ?

By the way, I grew up in the mountains in Colorado, and live there part time
now, so it's not like I "don't get" the scenario. I get it. This just never
made sense to me, unless you assume you've got bad (or poorly maintained)
brakes...

~~~
cracoucax
It's not about wear, it's about security and controlling your car... When
you're using your brakes you can't control your car nearly as well.

In case of emergency it makes a huge difference. You've got to control your
brakes and turn the steering wheel. If you release your foot from the brakes
the car will surge forward as it is not limited by the transmission.

Plus water, snow or ice downhill with brakes = danger, very easy to lose
control of the car. Again if you're limited by the transmission it's a non-
issue.

Plus it makes your brakes hot, which makes them less efficient in case of
emergency (I think).

Your transmission should always be the limiting factor of your speed, period.
That's it's job, to determine the speed of the car. The brakes are only here
for when you couldn't downshift fast enough and you're going to hit something
if you don't stop the car. That's what my instructor taught me, and repeated
again and again.

When driving I sometimes see people braking all over the place, most of the
time they also have strange trajectories, and are not easily predictable. I
pass them as soon as I can.

And yes I use a stick, never used an automatic in my whole life in fact. I
don't think it teaches you anything about the mechanics of the car as i know
mostly nothing about that. But I'm pretty sure you "feel" the car much more.
I'm nearly certain the aforementioned brakers are mostly automatic drivers
(pretty rare in my country as most people despise automatics.)

------
cosarara97
I think those smart programmers will be able to google after their Python
class, and dig both up and down. I learned Python, then C, and C++, and ASM,
and Javascript, and a bunch of others. With LISP, though, I always get bored
before getting to those awesome and powerful features and reaching
enlightenment. I guess I can only dig upwards.

------
jfarmer
Over the last three years I've personally taught hundreds of developers how to
program, either directly on indirectly. I've written curriculum at all levels.
I co-founded Dev Bootcamp ([http://devbootcamp.com](http://devbootcamp.com))
and am currently working on CodeUnion
([http://codeunion.io](http://codeunion.io)). I also am a board member and
instructor at Mission Bit ([http://missionbit.com](http://missionbit.com)).

I'm pointing this out because I have strong opinions about how to teach
beginners backed up by a non-trivial amount of success.

I disagree with Evan's conclusions, although I agree with the spirit of most
of it. I don't know how much experience Evan has teaching or what opinions he
has about how teaching/learning works, but where and how he misses the mark
are what I'd expect from someone who is a very experienced engineer but has
limited experience teaching a wide range of people how to program.

Here's my summary of Evan's article.

To be a good programmer it's necessary to have a mental model of how computers
actually do their work. Languages like Python, Ruby, JavaScript, etc. make it
difficult (if not impossible) for beginners to develop an accurate model. C,
however, forces the programmer to build an accurate mental model of what their
computer is actually doing. What's more, it isn't too difficult for ("smart")
beginners to learn as their first language. Therefore, we ought to teach more
beginners C.

I think his claim is actually a little stronger than that — he's almost saying
that teaching something other than C is a disservice to students — but I don't
want to be unfair.

I agree that students need a mental model of how computers actually do their
work in order to be a good programmer. I also agree that among all the
languages we have out there, C is the one that most closely approximate's the
computer's internal model of how it does its work. On Quora, someone asked me
what are the 5 language one should learn. At the top of the list was C. I
said: "Learn C. C is the best language to learn if you want to get a visceral
feeling for computation from a computer's perspective. " ()

I also recommended:

    
    
        - Ruby or Python
        - Scheme or Clojure
        - Haskell
        - Erlang or another "oddball" language like Forth, APL, or Prolog.
    

Link: [https://www.quora.com/Knowing-what-you-know-now-what-are-
the...](https://www.quora.com/Knowing-what-you-know-now-what-are-the-
top-5-computer-languages-to-learn/answer/Jesse-Farmer)

More generally, you want students to have an accurate mental model of
_computation_, not just how computers work. There are many perspectives and
one doesn't really "see" the overall picture until one has adopted, compared,
and related many different perspectives.

While I'll agree that Python or Ruby might not give a student an accurate
mental model of how a computer works on the inside, you won't be able to
convince me that they don't give students _one_ accurate mental model of
_computation_.

For a beginner, we're going from a place of no model of computation to some
model of computation. C's perspective on the matter is one perspective.
Haskell's is another. Smalltalk's is yet a third.

As a teacher, these are my goals:

    
    
        - Give a student _some_ accurate model of computation
        - Give them the tools to refine that model over time
        - Develop a sense of consistent forward momentum
        - Set them up to do all of the above without being in my presence
    

IMO, whatever language we teach first should be chosen with these goals in
mind. A beginner's first language is a beachhead. If we do our jobs correctly,
students will synthesize other perspectives as they develop as programmers.

I think Evan's rebuttal to this is encapsulated in this quote:

> So Python programmers, as a rule, never get around to learning C.

Well, maybe that's the problem! Maybe we should be teaching students in a way
where the _need_ to learn C is obvious to the student, where the student sees
a C-shaped hole in their programming knowledge (so to speak).

At CodeUnion, we're absolutely not shy about diving into the Ruby
interpreter's internals, for example. Want to know what a Ruby method does?
Let's look at the source code! It won't bite.

The problem isn't that students aren't eating their spinach. The problem is
that students aren't being exposed to situations where they see the lack of
spinach is the problem.

The same is true of more abstract concepts like data structures, by the way.
We can teach binary trees all day long, but it's not going to "click" for a
student until they have a binary-tree-shaped hole in their life.

Focus on getting them to that place and they'll be _begging_ you to teach them
C, data structures, functional programming, or whatever else you think is the
really important stuff.

~~~
e12e
This turned into a bit of a rant, but hopefully it does add something to the
discussion.

You make some interesting points, but in my (limited) anecdotal experience, C
is a pretty awful language.

Of all the languages I half-way know, I think I'm still least comfortable with
C. Why not start out by teaching assembler? Especially post 32bit x86,
assembler (intel syntax) is rather pleasant. It allows highlighting lots of
things that are commonly hidden in C: calling conventions, different types of
strings (eg: C vs Pascal) etc.

C is obviously very useful as a "real-world" language. If you need to add a
driver you need to Linux, or patch a bug in your shell (or shell utility) etc
-- knowing at least enough C to be dangerous can be very helpful. You'll
likely be able to put C to good use re-implementing critical sections of
python/ruby/whatever code. But IMNHO C still remains a rather awful language.

For some reason, I feel that pointers are easy in Pascal (and assembler) and
hard to grasp in C -- as far as I can tell this is entirely a syntactical
issue for me.

In the future (post rust 1.0), perhaps I'd recommend assembler, rust and some
other high-level language (or two...). I find it hard to recommend Pascal --
while compilers exist, it is a little too much of a "teaching language" \--
and a little too far on the outside of everything. Ada might be an interesting
option.

Perhaps assembler > C > python. Or assembler > SBCL (Steel bank common lisp)
-- because it integrates well with assembler, allowing the user to easily
"peek under the covers".

C is awfully low level -- and yet also surprisingly high-level. There's no
sane, canonical way to handle text (that is utf8/unicode -- not merely ascii).
I'm not entirely sure C++ is _much_ better in this regard, but I still think
Storstroup's contrast between "beginner C" and "beginner C++" from 1998 is
interesting[1]. I'd be curious what kind of production level code a beginning
C programmer produces, and for what kind of problems.

Fundamentally though, I'm not sure there is a good "one way fits all". A lot
of people will be served fine with only Python or Ruby (or Julia). Thinking
too much about how the computer works can also be a hindrance, I think.
Getting actual work done, does seem to hinge on joining together proven,
useful tools. Be that tool Berkley DB, SQlite, Postgres, levledb, Riak, redis
-- or text files (for example). Or protobuf or json or xml. Etc.

At any rate, I fail to see, from the point of "understanding how the computer
works" \-- it helps very much to learn (just) C. Without assembler, I think
most of the useful lessons are lost -- and I'm not convinced the number of
lesson most obvious in C -- but not covered by assembler on one end, and a
high level language on the other -- are that many?

[1]
[http://www.stroustrup.com/new_learning.pdf](http://www.stroustrup.com/new_learning.pdf)

~~~
jfarmer
For programmers familiar with Ruby or Python, my experience is that the main
points of confusion are all around what is happening in memory. What is a Ruby
String/Array/Hash/etc., _really_? That sort of thing.

It might seem trivial to you, but the idea that you have a bunch of 1s and 0s
in memory that the computer somehow "knows" is an integer or a character or
whatever else is a HUGE intellectual hurdle for people who haven't considered
it before. For that reason, I think it's a mistake to put a student in a
situation where they're grappling with both memory and things like calling
conventions simultaneously and for the first time.

I can't comment on Pascal, but I don't think C++ is an accurate reflection of
the computer's own perspective on how work is happening. The main thing C does
is make the way your program interacts with memory much more visceral.

There's the added benefit that the most popular interpreters for Ruby and
Python (MRI and CPython, respectively) are written in C. This gives the
teacher an opportunity to connect concepts in C _directly_ to already-
understood concepts from another context.

All in all, I think the debate about which language(s) to learn is overstated,
albeit one that programmers love to have for tribal reasons. The same concepts
are embedded in all of them in different ways. The important thing is
extracting, isolating, and abstracting those concepts.

------
justin_vanw
> Even if we’ve never seen the inside of a gearbox, we know what the Overdrive
> gear in automatic-transmission cars means, and why you use it to pass a
> truck on the highway.

Wow, such a bunch of self righteous bullshit! Overdrive is either a
specialized gear or just the highest gear. It is in all cases the worst
possible gear to 'pass' anybody, or really do anything besides maximize
efficiency while cruising at a constant speed.

People that drive stick aren't better drivers or better at driving, evidence
of which is the pile of prematurely worn clutches behind every lube shop.

------
georgeoliver
It seems like this article makes the tacit assumption that 'better software'
means the programmer is closer to the metal and has a better mental model of
computation. I won't argue that's not one aspect of better software.

But doesn't that completely ignore the rest of what makes good software, like
understanding the problem space and creating a good design? It seems to me
that your choice of language only matters if the language muddles your clarity
of thought and expression, and in this regard the merit of C over Python is
less clear.

------
ajuc
I've heard similar arguments made against BASIC, C and Pascal - "learn
assembler, real programmers code in assembler, you need to know assembler to
understand what's really happening".

Good to know now at least C is allowed. We're going somewhere :)

As to losing interest in programming because Python is too easy - BASIC on
8bit computers was gateway drug to programming for me and most of my friends,
I think kids like programming being too easy (instant feedback etc).

~~~
kazagistar
Yeah, the easy argument struck me as a bit backwards. I mean, it is basically
always possible to make programming harder for yourself if you don't have
enough of a challenge.

------
al2o3cr
"they will hit a layer of C code and won’t know what to do next."

Really? "Smart and curious" people will just see this new language and go
"well fuck, I dunno what to do"? They won't, for instance, just LEARN THE NEW
LANGUAGE?

Arguing that beginners should start in C because they need to "understand how
computers work" is like arguing that first-graders should be taught the
construction of the reals so they "know how arithmetic works".

------
mxfh
That's quite a weird analogy, being raised with the still ubiquitious manual
shift gearsboxes of german cars, i still need about two minutes to figure out
modern cars with automatic shift every time I rent a new model, OK I learned
that break on N thing pretty soon, but still. This adaptation process goes
both ways.

The true assembler equivalent candidate for gearboxes is the infamous
"Zwischengas" or double clutch on unsynchronised manual transmissions.

------
jqm
Not everyone wants to know how cars work. Some people just are looking for the
simplest way to get from point A to point B. And that's OK.

------
squeaky-clean
I think a big problem is people not distinguishing "Programming" from
"Computer Science". Sure, Python isn't the greatest for understanding how a
computer functions, but like the author says, it's very fast and productive
and simple. You can be a programmer, but not a computer scientist, and I think
that's just fine.

------
dools
C??

Stick shift??

I learned to code on a PIC micro in assembly.

And I learned to drive in a column shift Kingswood with one break pad, no
handbrake and a missing accelerator peddle.

Kids these days.

------
TheLoneWolfling
> Python programmers, as a rule, never get around to learning C

Counterexample: I started with Python, learned C, and switched to C for most
things. (Python is far too slow. PyPy is better, but still...)

------
dmritard96
depends on what you are optimizing for.

You might be optimizing for the ability to write the most righteously perfect
software, down to the number of registers and getting every ounce out of the
bare metal.

Or you might optimize for getting more people involved in programming as it is
a truly transformative skill that empowers and enlightens. I know which one I
would prioritize and which one is probably premature.

------
antirez
“One day you’ll be in Europe”

------
DavidWanjiru
I will make an analogy with music. If you are a complete novice in playing any
musical instrument, I could teach you the three guitar chords that you need to
learn to play "She'll Be Coming Round the Mountains" in two minutes. Depending
on how long it takes you learn how to hold those chords and change between
them, you could come from having no clue whatsoever to playing something
recognizably related to a girl coming round the mountain inside two hours,
perhaps even less depending on your learning speed. I could then teach you the
other remaining five major chords, as well as how chord progressions work
(play the 1st, then 4th then 5th chord in any progression for example), and
once you've mastered how to hold and shift between all seven (in a week? a
month? three months?), I'd now have you coming round the mountain in seven
different keys. The advantage of this approach is that it gives you, the
complete guitar/music novice, the satisfaction of being able to play something
you and your dog recognize as music almost as soon as you start out. Are you
an accomplished musician yet? NOT EVEN CLOSE. How far do you have to go? Very
very far. Eventually, when you understand how music works in general and in
particular on the guitar, you'll get to see how and why I taught you those
three first chords, and why a I, IV, V chord progression works but a I, II,
III makes your dog go for a walk on it's own. You'll be able to build your own
chords! You'll even be able to argue with me about the correct name for some
given chords! You'll be able to play melodies from chords, maybe even play
both chords and melodies at the same time! [0] So, if you wanted to learn how
to play guitar, would you want to first get a years worth of music
conservatory work under your belt before you can belt out the simplest of
tunes, or would you rather belt out "Coming Round the Mountain" in a few hours
and then figure out later how she got round the mountain? Which one do you
imagine will be more satisfying? I reckon it's the same with programming.
Python may not teach me how to write my own "chords" or enable me to get into
arguments (valid ones) about stuff, but it enables me to do something visible
almost immediately, and I can take pleasure and motivation from that initial
success. Then as I go on, I'll dig into how the stuff actually works and
develop a better understanding, and this deeper understanding, just like in
music, is a lifelong pursuit. Just as guitar masters are still learning, so
are those of you who are, by any measure, programming masters. So I'd say, if
you're teaching someone, or learning, start off with something where it's easy
to produce something tangible easily. Then develop a deeper understanding as
your progress demands this deeper understanding. [0]
[https://www.youtube.com/watch?v=5vCcZIARw9k](https://www.youtube.com/watch?v=5vCcZIARw9k)

