Hacker News new | past | comments | ask | show | jobs | submit login
You can't dig upwards (evanmiller.org)
152 points by plg on Dec 7, 2014 | hide | past | favorite | 144 comments



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.


I think you should learn C to learn how computers work and learn Haskell to learn how languages work. Those two are remarkably different ball games despite the supposed identification of the two that persists today.


I'm tempted to say you should learn C to learn how computers work, and Haskell to learn how computing works.


I'd say that for one of the terminating languages (Agda, Coq) perhaps?


It helps a lot to be exposed to a variety of approaches, exactly as you say, and to be exposed early while you're still forming ideas about how things work.

Simply saying C is the "best" way also misses that point that different people are motivated to learn in different ways. For many, ramping up quickly in Python and plugging something into a Twitter library would show them that they can actually do something interesting right now, which is enough to keep them coming back for more. Others are driven by understanding how things work, and would be happy to crack open a core dump to figure out what happened.


I agree that C is not the "best"; just, vital (I buy that Rust might supplant it.)

But there's nothing you engage with in Python that you can't get by learning C, or Lisp.

Like I said: Python is just a bad example.


At the risk of being perceived to move the goalposts, I think there is a real lesson to engage with: empirically, for large classes of programmers, syntax, uniformity, and absence of boilerplate actually make a larger difference in out-the-gate productivity than many would like to admit. I understand that this isn't a lesson about programming per se, but I think it's a lesson about the practice of programming.

I see this repeatedly in our internship program, where people often ask after we "promote" them to other language work "Why can't XYZ look as clean and simple as Python?". I do think that's a lesson worth learning, and it's a little hard to learn except by osmosis. If I just tell someone "empirically, syntax matters a lot more than you might think in doing real work" it's easy to dismiss as understood-but-not-important. Jonathan Blow has a bit in one of his videos about "understanding" vs "really viscerally understanding", and I think this is one area where the latter simply can't be achieved without direct experience.


Productivity, yes! Understanding, no.


I should have been more clear: I agree with you on this and think C should be one of the batch of first languages students are exposed to (as the best choice in its class at the moment). The OP seems far too biased towards C, though.

What worked for me was early exposure each of: a specific teaching language (somewhat like Eiffel, within a simplified environment), C/C++ and Scheme. They each taught me very different things and I'm glad they were all offered in my undergrad, although I think they should offer Scheme earlier (IIRC it was part of an optional third year course, which also touched on e.g. Prolog).


I agree. The only problem I have is context switching between languages. I write in C, PHP and Javascript (I know not the best three options, but I have yet to have a problem I can't solve using one of these three). I find it takes me a couple of hours to get back into the full swing of each language and these three are easy to context switch between. I worry that if I had a true high level language to my tool kit that I will grind to halt.


I find switching between Algol family based languages easy (Basic, Pascal, C, C++, Javascript, Java, C#, Go, PHP, Lua, Rust). But switching between Algol and various Lisp dialects takes some minutes.


That is impressive. I find that it is the little things that I end up messing up on and spend more time using my delete key than any other key until I get back into the swing of things.


I think object-oriented programming counts as something you can learn from Python, Java, Ruby, or PHP5 -- something you wouldn't learn writing Lisp, ML, or Haskell. Sure, Lisp optionally has CLOS and Haskell has typeclasses (which are somewhat different from traditional OOP). But if you program in Java you will be forced to think in an object-oriented way.

You could make the argument that OOP is not a fundamental concept of programming, but you could say the same for monads. Even if it's just a productivity tool or, as pg argues, a substitute for genuinely intelligent programmers, it's still something. It's certainly something that employers expect you to know.


It's not about what they're useful for; it's about how they make you think about the whole. It is a more complete understanding.

Knowledge does not need to be directly applicable to be immensely valuable.


For me, Lisp and Haskell exercise two different conceptual muscles. And neither of them, nor C in general exercise the increasingly important concurrency muscle. Maybe Clojure is a good way to get both the Lisp and concurrency fixes, but something on the Erlang VM, or perhaps Go or Node, are more fundamentally designed around concurrency, and many people build those chops using threads in C++ or Java. Interestingly, this is another area where the usual suspects of Python, Ruby, and PHP are quite poor.


What about Go or Node make it more designed around concurrency than Haskell? It sounds like you are trying to say Erlang, Go, and Node have "first class concurrency". I'd agree that Erlang does, but I'd say that Node and Go do not. I'm almost tempted to wager the same is true for Lisp, but don't have enough experience with concurrency in Lisp.

Furthermore I'd wager that Go and Node have nothing in the realm of concurrency that Haskell doesn't have. If I'm wrong, I look forward to finding out ;)


"Languages aren't defined by what they make possible, but by what they make easy", to which I would add "and idiomatic". Go and Node make their (very different) concurrency features easy and idiomatic. Maybe Haskell does too, though, and I just don't know it well enough!


Here's 2 Examples (Warning: Didn't run them and could have missed imports).

One parallelizing a sudoku solver and the other making a reminder program concurrent. Both are from Parallel and Concurrent Programming In Haskell[0].

Sequential version[1]:

    import Sudoku
    import Control.Exception
    import System.Environment
    import Data.Maybe
    
    main :: IO ()
    main = do
      [f] <- getArgs
      file <- readFile f
    
      let puzzles   = lines file
          solutions = map solve puzzles
    
      print (length (filter isJust solutions))
Parallel version[2]:

    import Sudoku
    import Control.Exception
    import System.Environment
    import Data.Maybe
    import Control.Parallel.Strategies (parMap) -- line that changed
    
    main :: IO ()
    main = do
      [f] <- getArgs
      file <- readFile f
    
      let puzzles   = lines file
          solutions = runEval (parMap solve puzzles) -- line that changed
    
      print (length (filter isJust solutions))

Here's an example making a small reminder program concurrent:

Sequential[3]:

    import Control.Concurrent
    import Text.Printf
    import Control.Monad
    
    main =
      forever $ do
        s <- getLine
        forkIO $ setReminder s
    
    setReminder :: String -> IO ()
    setReminder s  = do
      let t = read s :: Int
      printf "Ok, I'll remind you in %d seconds\n" t
      threadDelay (10^6 * t)                  
      printf "%d seconds is up! BING!\BEL\n" t

Concurrent[4]:

    import Control.Concurrent
    import Text.Printf
    import Control.Monad
    
    main = loop
     where
      loop = do
        s <- getLine
        if s == "exit"
           then return ()
           else do forkIO $ setReminder s
                   loop

    setReminder :: String -> IO ()
    setReminder s  = do
      let t = read s :: Int
      printf "Ok, I'll remind you in %d seconds\n" t
      threadDelay (10^6 * t)
      printf "%d seconds is up! BING!\BEL\n" t


 
0: http://chimera.labs.oreilly.com/books/1230000000929/index.ht...

1: http://chimera.labs.oreilly.com/books/1230000000929/ch02.htm...

2: http://chimera.labs.oreilly.com/books/1230000000929/ch02.htm...

3: http://chimera.labs.oreilly.com/books/1230000000929/ch07.htm...

4: http://chimera.labs.oreilly.com/books/1230000000929/ch07.htm...


Thanks!


When you say "concepts that a language is uniquely good at engaging", they might get in the way for a novice learner. Python and such are pretty good at the very basics of programming.


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.


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.


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?


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.


Thank you very much m0nastic, it's a great explanation.

Now I can see the mathematical elegance of the haskell code.

An explicit algorithm of the same (pseudo)code (using a mutable list) could be:

   function qsort(list)
     if list is [], return []
     let x = extract_first_element(list) // car 
     let smaller = [a | a <- list, a <= x]
     let larger = [b | b <- list, b > x]  
     return qsort(smaller) ++ x ++ qsort(larger)
And by writing this pseudo-code, I can see that in the haskell code, "smaller" and "larger" are clearly parallelizable, while in the "imperative"-"explicit algorithm" pseudo-code, the compiler cannot do such optimization...

And then, I'm enlightened. Thank you.


(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.


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.


Pure functions can very much be algorithms. Functional programming is just that and can include algorithms. Monads are not intrinsically related in any way to impurity, they are an instrument of control/data flow. They can be used to apparently cause side effects but do not actually introduce impurity.


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


Right, since I have the exact opposite experience and I can find a logical explanation for it.

I learned to drive in an automatic transmission car. I was still in terror when first learning to drive that car. Later on I got a new car, and since manual transmissions where cheaper I got a manual one. I was again terrorized every time I had to stop and get into first gear again, or when I was stopped in a hill. I can only imagine the absolute nightmare I would have been trough if I had to learn both how to operate the stick shift AND the rest of the car and laws of the road at the same time. I actually think progressive learning was better for me.

Same with programming, I learned GW Basic as a kid doing games and what not. Then switched to Pascal, then C and then ASM. I can only imagine how motivating it would have been to 10year old me having to write pages and pages of ASM just to do the simplest thing. Instead learning a easy high level language helped me stay motivated and learn progressively getting into harder stuff little by little.


Indeed. For every anecdote about "starting with assembly and working up" there is another about "starting with scripting languages and working down".


You're right that this is all just posturing and anecdotes.

For every anecdote of a Python developer who doesn't have the slightest idea of memory management, i can bring out an anecdote about a C developer who learned Perl and wrote C in it because he didn't trust hashes and had no idea of how to capitalize on high level algorithms.

It's also important that programming is still very young. The entirety of the human race has only been programming for about 3 generations, and only now are the first programmers beginning to die of old age. It will be a long time before we actually have enough data on what works and what doesn't.


I think a better message to send to programmers is not "don't learn Python as your first language", but rather "don't think Python is the only game in town". People should never suggest that you should pigeonhole yourself into one language or one programming paradigm. I know many people who learned Python as their very first language and are currently proficient in languages like C and Haskell and Scala.

If you really care about cars, you should learn a stick shift. If you really care about programming and computing, you should learn C. But just because you learn an automatic first, to ease you into driving, doesn't necessarily mean you're any less likely to go and learn to drive a stick. If you're really interested in using and understanding cars, you'll realize automatic is not the end-all-be-all. The same is true for programming.

>It's also important that programming is still very young. The entirety of the human race has only been programming for about 3 generations, and only now are the first programmers beginning to die of old age.

I often think about this too. What languages will be the gold standards in 30 or 50 years from now? Or 100? Will people view Python and Ruby as many people view COBOL and Pascal today?


> I often think about this too. What languages will be the gold standards in 30 or 50 years from now? Or 100? Will people view Python and Ruby as many people view COBOL and Pascal today?

It's hard for me to believe that they will view Python and Ruby as charitably as we view COBOL and Pascal. Cobol is only 32 years older than Python (1959/1991) and Pascal only 25 years older than Ruby (1970/1995).

I imagine the gold standard programming language for the 2040s is either a twinkle in someone's eye or just invented.


"You're right that this is all just posturing and anecdotes."

Maybe in programming, but I think there are plenty of areas wherein this "manual transmission" learning curve should be practiced.

I hate to stomp all over this guys grave, but that air france pilot who responded to the STALL STALL STALL blaring in his face for two minutes by pulling up on the yoke as hard as he could ... perhaps that guy could have used some more "manual transmission" knowledge of the plane.

http://en.wikipedia.org/wiki/Air_France_Flight_447#Human_fac...


That was a much more complex situation, and fundamental knowledge about airplanes (that all of the pilots probably have plenty) wouldn't have helped anybody.

From that same link.

"this led to a perverse reversal that lasted nearly to the impact: each time Bonin happened to lower the nose, rendering the angle of attack marginally less severe, the stall warning sounded again—a negative reinforcement that may have locked him into his pattern of pitching up"


The author's micro-elitism about stick shift is one of those things that I, as a manual-driving Brit, find ridiculous. It's a skill we almost all learn here, not something special. Our (low) accident incidence is usually ascribed to drinking, visibility, or speeding; and speeding tends to be a result of someone thinking they're a better driver than they really are.

US driving is IME (Boston, SF) easier than UK driving because there is much more road space and larger parking spaces, lower and more frequently varying speed limits, and less aggressive driving.

So that's my experience. Back to programming: we would do well to look at "countries" other than our own when talking about all programmers. What about embedded-land? The endless rolling plains of LOB internal applications? Stranger languages: J/APL? Labview?


I do drive a stick-shift car, and I've also driven (rental) automatic cars.

A stick-shift get you way much closer to the engine. A stick-shift driver can show it's emotions via the engine. stick-shift invites more aggressive driving. Your mental state "shows" thru your driving.


I learned on an automatic but quickly learned to drive my dad's car which had a a manual, and my first car, an air-cooled Beetle, was a stick. I also feel that driving a stick does make one an overall more aware driver. It's also why I'd never want a self-driving car.


Aware of what, though? And for what purpose? Being aware of the engine is not the same as being aware of your surroundings and hazard perception. In fact you can watch this in learners; at a junction which demands lots of traffic observation they make errors in moving off, or (more dangerously) fluff a gearchange while driving along and then look down or spend a few seconds panicking and recovering during which they might miss something happening in front of the car.

The programming language equivalent is clearly memory management. Yes, you can make it more efficient if you do your own allocation - but you have to get it right every time, and C offers you very little help to do this. C++ offers more, and Rust holds out the prospect of getting it right provided you can understand and fix all the compile-time errors. If memory allocation isn't critical to what you're doing this is a distraction that consumes time and effort to no result.


Not quite following the stick/automatic and human/computer comparisons. I also drive a stick - and I definitely think it makes me a better driver - but I also think that a computer could do a better job than me. The computer could be tracking many more things - environmental conditions, 360-degree obstacle detection, etc. - than I could ever do myself.


But what it doesn't do, yet, is know that you're going to take that hill home, so let's leave it in 3rd gear for the time being. Or that you intend to accelerate out of that next curve at the edge of traction, so let's not upshift as we ease up on the gas.


Desired power is really a one-dimensional intent, manual and traditional automatic transmissions are both leaky abstractions one of which forces a two-dimensional input, and the other of which gives a choice between a 2D input with different characteristics and a 1D input that isn't continuous but instead behaves very differently in different input ranges. CVTs are less leaky than either.


Desired power is one of many intents, the others include torque, acceleration, grip, and comfort.


Wait, maybe I can understand not wanting to drive automatic, but fully self-driving cars let you actually do something entirely different with your time.


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.


I agree with you on (1). Knowing more is better than knowing less; this is almost tautological. Knowing C -- it doesn't matter whether it's your first language -- will make you a better programmer.

However, I disagree with the assertion from TFA that knowing C is somehow more "intelectually stimulating" than knowing Python. Specifically, I disagree with this:

> "I get the sense that Python-first programmers are missing out on a lot of the fun of programming. There’s a mistaken notion that programming is most enjoyable when you’re doing a diverse array of things using the smallest amount of code, leveraging client libraries and so forth. This is true for people trying to get something done to meet a deadline; but for people motivated by learning — and who aspire to be extremely good programmers — it’s actually more fun to create programs that do less work, but in a more intellectually stimulating way."

I find the above assertion ridiculous, and I know C better than Python. Learning programming with Python is more fun than learning with C. That's not to say C isn't fun; programming is fun as a whole. But Python is not for people "trying to meet deadlines", and it's way less puzzling and lets you get into the "interesting" part of your project faster, with fewer pitfalls and less boilerplate than C. That's the definition of "fun".


People who know C are better programmers. I would say that also people who know assembly language are better programmers. People who know a functional language are better programmers. People who know/have used a variety of languages are better programmers. Monoculture of any language or level of abstraction will limit programmer achievement.


(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.


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".


Here's some data:

http://scholars.opb.msu.edu/pubDetail.asp?t=pm&id=7795212016...

When Michigan State University switched their first course from C++ to Python, the Python students did just as well on the 2nd C++ based course as those who took the first course in C++.


Here's the relevant raw data from that, but no individual scores or a distribution are provided:

    CS2 final exams in SS08
    Group       n   Mean   StdD
    Python     21    172     40
    not-Python 62    177     24

    CS2 programming projects in SS08
    Group       n   Mean   StdD
    Python     21    324     82
    not-Python 61    340     56
It really depends on their definition of "significant", but just from this data alone I see the not-Python group clearly beating the Python group on the two items here (and with lower variance.) There might be too much noise here to see a big correlation, but there's also the possibility of the data being "below the noise floor", so I would not draw too much conclusion from this.


According to the paper, that could be explained by the Python students having lower GPAs.

"In addition, multiple regression analysis showed that students' past performance (overall GPA) in the University predicted final grades, final exam scores, and programming project scores for the course, but there was no effect of the programming language feature: Python or non-Python."


"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."

I've interviewed (and worked with) a lot of programmers now, and I can definitively say that the best ones know how to program in C, and the worst ones do not. I'm guessing that Evan Miller has had similar levels of exposure to good programmers and bad programmers. I make no claims of causality, but the correlation is a damned strong hint.

Said another way: data actually is the plural of anecdote, for sufficiently large values of N.


It's not as strong a hint as you think. I've interviewed and worked with a fair number of programmers myself, and I can say equally definitively that the best ones know how to program in Lisp, and the worst ones do not.

All we see is that the programmer who is motivated to teach herself C or Lisp (or any other "difficult") is a better programmer than the programmer who is not. Which, on further reflection, is almost self-evident. Of course programmers who are motivated to keep learning and keep practicing become more skilled. But it doesn't make any special claim about the benefit of C in particular.

Coming at it from another angle, have you worked with a lot of electrical or computer engineers? C, in many cases, is the highest-level language they know. From personal experience, I can tell you that their code is no better (and in many cases, considerably worse) than code written people who've had experience with languages at the top of the abstraction hierarchy.

EDIT: And, of course, I scroll down to see that tptacek has already made this exact point, and more eloquently, to boot.


I remember that understanding pointers requires a special mental effort, it changes your head, and then can be used to clearly separates programmers who can think in pointers and programmers who don't.

In this regard, "pointers" in CS can be analogous to "stick shift" in driving cars.

This remind me of this article of Joel on Software:

"All the kids who did great in high school writing pong games in BASIC for their Apple II would get to college, take CompSci 101, a data structures course, and when they hit the pointers business their brains would just totally explode, and the next thing you knew, they were majoring in Political Science because law school seemed like a better idea"

s/java/python

http://www.joelonsoftware.com/articles/ThePerilsofJavaSchool...


I never understood this. Pointers just aren't that difficult if they're explained well.


That is after the first language though. If you started in Basic or Python or Lua or whatever, it does not mean you cannot familiarize yourself with C and C++ later.


>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.

Much of the data I've seen has suggested that, of all the hard sciences, "engineering" is the one most fraught with magical thinking. It seems to be populated by people looking to shape the problems they face to the solutions they already know -- whether or not those solutions are the best that can be reasonably implemented.

It is the worst part of my experience transitioning from physics to software: everyone seems to go about citing their clever catch-phrases as fact.


In France, they drink.


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)

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)


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.


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.


I'm glad there were other nerds who were bothered by this like I was. Thanks HN!


Yes, this was very jarring, leaving exactly the opposite impression the author was aiming for at that point! My memory of learning to drive a manual was also not nearly as fraught as the author describes, and I don't claim any particular driving prowess (this could be a function of the car, though, I learned on one that I suspect was more forgiving).


I can confirm that the miata's is an exceptionally unforgiving transmission. Fun drive once you get used to it, though.


Overdrive is what you use to ride into the danger zone.


That's only true in the cases where you are at the same moment jumping off the deck.


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.


> 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.


Indeed. I remember clearly asking my high-school teacher why, if photosynthesis was so simple, didn't we use it to produce sugar artificially? I felt a little irritated that it turned out we spent so much time, and put so much emphasis on such a simplistic model... when apparently it wasn't very useful for any kind of analysis/further work etc.

There is absolutely an art to finding the correct way to simplify models. I'm reminded of Alan Kay's comment at his Ted talk, where he holds up the Gapminder-project for a useful simplification and highlighting/illustration of (rather complex) data, and derides a 3d model of biological processes in cells -- because they show proteins magically dancing stately around and matching up to form reactions -- which is completely different from what is actually happening (large complex molecules spinning really fast, thus making the incredible improbable matching of to such proteins just so possible).

That said, when it comes to mechanics, I think most realize that the model is naive -- students don't feel "deceived" -- and the ratios etc do actually translate to some useful intuitions when doing real-world work (eg: clearing rocks with wire, winch and tackle).


> 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.

Truer words never said. I'm one of those data points.

Luckily I had the fortune of having a science teacher who identified this and did teach me the basics of the underlying theories in high school. My mind rejects "rules" and wants "reasons." While everyone else was learning about Newtonian gravity, I was grasping the basics of special relativity. While everyone else was learning the "rules of bonds" in chemistry I was learning the basics of QCD. From these base theories I was able to remember via back-tracking all the way back up to the "rules" that I had learnt along with the rest.

It's about being a student versus being a parrot. Wanting insight into the underlying complexities is being a student while regurgitating information in an exam is being a parrot.

I started off with VICBASIC at 8, then QBASIC at 10. I only started C# when I was 16 or 17. Even then C# has a lot of abstractions away from the hardware. The only time I ever truly learnt what was going on is when I was thrown into the deep end: a customer was having problems and I had a memory dump, WINDBG and SOS. Only recently have I been taking the time to actually learn C/++.

Now that I am no longer "parroting" code I am starting to identify parroted code. No care is given to memory usage "because the GC will deal with it." Mutexs are sprinkled everywhere "because that's how you deal with concurrency."

At some point every developer needs to be thrown into the deep end. I would say that learning Python as your first language is fine.

However, if you never take the time to learn the nuances of how Python works, you are a dangerously ignorant.


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.)


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.


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.


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.


Well, what?

The massless ropes that do not stretch and massless pulleys and cogs are great approximation for the real world in nearly everything available at home.

If kids can not relate to those, it's because they don't look into the machines they have around them, not because the model isn't realistic.


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.


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).


Reading this made me think that the author isn't very familiar with how a transmission works, either. Overdrive to pass a truck at speed? I think not.


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?


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...


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.


> 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?


And neither high level languages mentioned in the article (namely python and java) handle any of these features anyhow.


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


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".


> 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.


> Which is, all things considered, not that great a criticism, seeing as how few modern languages are terribly optimized for these cases either.

This is true with a but; it's true in the extent that it's not like C gets it worse than the average high-level language, but the current selling point for C is that it's "close to the hardware" or "portable assembly" and the less that is true, the less strong C is.


In this meaning I don't consider the statement correct.I don't know a language which is closer to the machine.

Assembly does not count, because it is not a single language. Multicore is mostly a library issue and C11 is as close to the machine as any other language here. There are languages aiming at distributed programming (Erlang,X10), but none of them claims "close to the machine". SIMD is bolted on in C, but no other language does better. Caches are supposed to be transparent; Are there languages with explicit support for scratchpad memory?

This leaves GPUs. OpenCL is supposed to be the solution here. Would you consider OpenCL closer to the machine than C?


also: pipelining, "advanced" instructions


The news UK 14-16 Computer Science course has a bit of simplifed ARM assembly, and a high level language, for the course I'm teaching I could choose from C, C++, Java, or Python. I picked Python for them, that's what most of the available materials are for also. However I rewrote the suggested lesson plans to do transistors, logic, how a cpu works and assembly first, and then move onto Python, so I could always say "and this is how this works as a lower level"


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.


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.


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.


> 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.


> 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


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.


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.


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.


Knuth would agree with this, right?


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


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.


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?


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.


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.)


"C" is a thin layer over assembler language.

BASIC (if remember correctly, has no pointers) so it is far from assembly. BASIC seems close to Python in this regard.


> BASIC (if remember correctly, has no pointers) so it is far from assembly.

BASIC, like Assembly, has no special "pointers" -- just values (numbers) that can be treated as addresses and used to read (in via PEEK statement) or write (via POKE statements) the corresponding memory locations.

C, by having distinct pointers but supporting arithmetic on them is somewhere between languages like BASIC or Assembly (where there are just numbers, which can be used as addresses, but not special "pointers") and something like Python (where there are object references, but you can't do arithmetic on them like C pointers.)


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.


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.


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.


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.


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.


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"?


> 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"?

Because that's doubling the amount you have to learn in early CS courses. Most students already struggle with wrapping their heads around basic programming, algorithms, and data structures. Adding the challenges of pointers, memory allocation, etc. into the mix can only drive up dropout rates.

I don't think anybody should graduate with a CS degree if they don't know C, but I also don't see any value in forcing them to learn it first.


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.


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


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.


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


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.


I've only done a UK Diploma in Computing, and then only as a part time course (OU), however we started with C and a hardware module (temperature sensor connected by parallel port - that dates it, though they were on their way out - IIRC it was the last year they used that module) and learnt binary operations, some assembler, memory addressing schemes and what have you. It was a great way to introduce the computer as a general purpose device and be able to link the computer language back to assembler and machine code and ultimately to physical operations on binary data within the processors and memory/RAM.

The other modules I took covered OOP using C++; Smalltalk, SQL, db design (JNF and all that jazz). We're going back more than a decade here so no doubt there was a whole heap more too.

Personally I liked starting with the hardware, jumping in with C and then linking the two together. Gave a good basis to understanding computers, I think that first module might have been called something like "understanding microprocessor based computing".


"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...


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.)


No one seems to be answering the question correctly, so here goes:

On a long descent, excessive use of the brakes can cause them to overheat and "fade", meaning that you will not be able to stop the vehicle at the bottom of the hill. This can be caused by loss of friction from overheated brake pad material and rotors, or by the actual brake fluid itself boiling out of the master cylinder and becoming compressible.

This is not nearly as prevalent as it used to be, now that vented disc brakes are standard on most modern vehicles.

See http://en.wikipedia.org/wiki/Brake_fade

Another reason is that fuel injection is cut completely in modern fuel-injected vehicles under compression braking, meaning you will get better gas mileage if you engine brake than if you idle down the hill.


If your car has not ABS, you can slow-down without losing directional control by using the engine. In a slippery slope, if your wheels block (because of braking) you lose the ability to turn to avoid obstacles.


Before ABS brakes were ubiquitous this was more important advice. Most cars with ABS standard advise against engine braking in the manual.


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.


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) and am currently working on CodeUnion (http://codeunion.io). I also am a board member and instructor at Mission Bit (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...

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.


I hugely agree with this. I first learned Java in a year long high school class. As soon as the class was over, I knew I liked programming and wanted to become a better programmer, but I also had the feeling that there had to be something less painful that Java to program with. This lead me to Python, which I used for everything over the next two years. From Python, I also learned web programming with Django and Flask.

Eventually I decided to try to write an IRC bot in Python. During this effort, I realized I knew nothing about networking, concurrency (how does gevent work?), operating systems, and how to do non-trivial IO. Similarly, I also ran into performance and memory problems with some numpy code, and I started to wonder how does this stuff work and why is numpy so much faster than anything I can write in pure Python? These were my "C shaped holes". Around the time I discovered I had these "C shaped holes" is when I started college, which was a good place for me to take low-level programming classes and fill the holes.

Besides getting a student motivated, maybe the hard part of education is having students realize that they have these holes.


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


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.


> 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.


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.


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).


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.


"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".


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.


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.


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.


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.


> 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...)


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.


“One day you’ll be in Europe”


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




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: