1. 26

  2. 10

    I think he makes an excellent argument for making Zig the introductory language, simple clear semantics and the notation is made to describe allocations / directly interacting with the memory. It doesn’t have a repl though :/

    Then at some later point make them write a lisp and teach them Haskell then you’ve got a solid base for learning anything. Rust is dismissed by having unique semantics / control structures. JS/TS by too much noise. Maybe ReasonML? Cleaner syntax than OCaml and has mutability…

    Overall I think his point about academia ‘guiding’ industry by choice of language to teach outweighs everything else. For this reason alone I would pick either a C replacement (Zig) or a Java replacement (which to be fair Scala is, but that is also a leaky abstraction, I would go with Haskell personally).

    1. 3

      OCaml has relatively ‘easy’ mutability too: https://dev.realworldocaml.org/imperative-programming.html

      1. 2

        Zig or Nim would be great introductory languages. Nim in particular lets you start higher level (whereas afaik Zig makes you worry about more low-level details up front like allocators), but runs the full gamut of what a systems language can do, supporting pointers, manual memory allocation, and so on.

        1. 8

          Overall I think starting higher level to learn is better if you want a broad overview.

          1. 4

            Overall I think starting lower level to learn is better if you want a solid foundation.

            1. 2

              I do think it’s important to understand the concepts which underpin the abstractions. However, most programming is done in relatively high-level languages. In this situation, I believe it is much more important to write what you mean, rather than trying to express how you think the underlying hardware works.

              If you teach someone the higher-level way to do something, then later show them how it might itself be implemented, they’ll probably keep using the high-level way, but will know how to drop down a level on the rare occasion that it becomes necessary. However, if you teach them the low level implementation first, that becomes “normal” for them, and they will keep reimplementing the wheel all over the place. When you show them the higher-level construct later, it could be viewed as an unnecessary curiosity, because the user has become set in their ways.

              1. 1

                You’ve convinced me that every JavaScript class should start with a few weeks of APL

                1. 1

                  It depends on how long you stay at each layer.

            2. 1

              Why not start with simplified machine code, then move to assembly, then C, etc.? That’s what my undergrad EE curriculum did in 2005-2009.

              Given, I had already had 2 years of Java in highschool CS. But I didn’t learn much of value from that, except for how to do numeral base conversions.

              1. 4

                I don’t think that assembler is required or helpful when teaching fundamental algorithms and data structures, and at worst it just makes it harder for less experienced students. Ideally, whatever language is used, the student should never have to think “I know how to do it, but I don’t know how to implement it” – not “implement it well”, but just have a working algorithm.

                1. 0

                  If the student doesn’t know assembler, it will be hard to understand the C ABI which is so commonly used in FFI’s to make languages interoperable. So I think you’re contradicting yourself. If you don’t know assembler, you don’t know how to implement everything. And what about boot loaders that the student will be using every day?

                  Also, history is important. If you know the story of how people used to work before “high-level” C programming became popular, it gives you perspective which is applicable when deciding whether an abstraction is a productivity boost or not. Tony Hoare gave an interesting talk with all sorts of explanations of why null-pointers became popular. You can only understand the world if you understand the history.

                  1. 4

                    I’m not saying they should never learn it – just not in introductory CS. I’ve thought people in such courses, and there are far too many who haven’t ever programmed at all or not enough to understand basic concepts such as recursion. Overwhelming them with FFI’s and ABI’s before they can even implement binary search on an array won’t be manageable, either for the students or the coordinators.

                    1. 1

                      I really think you should teach things from the fundamentals and move up.

                      1. 1

                        I agree, just my fundamentals are computational thinking familiarity with how computers “think”.

                    2. 1

                      If the student doesn’t know assembler, it will be hard to understand the C ABI which is so commonly used in FFI’s to make languages interoperable.

                      In most languages, you can use the FFI, call C functions from another language and even call the other language’s functions from C, without encountering even a whiff of assembly.

                      And what about boot loaders that the student will be using every day

                      Unless they are studying some sort of embedded programming, why would they have anything to do with bootloaders? The subject of the original article is an introductory CS course that would be taken by a broad range of students. Writing your own bootloader probably belongs in a less general course, along with assembly.

                2. 1

                  I always wonder when people say ReasonML has a “cleaner” syntax, what do they mean precisely?

                  1. 1

                    It means they like the syntax because it is more familiar, that’s it. For example, it means they prefer f(x, y) to f x y. This kind of superficial differences shouldn’t matter, but in practice, it matters A LOT, in fact having the familiar syntax may be one of the most important thing language needs to have to be popular.

                    1. 2

                      Well, that’s what I also think, but I’m curious to hear from people who say that. I haven’t seen Elm fans say “I love that language, if only it had f(x, y), not f x y for example.

                    2. 0

                      I guess ‘clean’ == no semicolons xD

                      Thanks for calling it out.

                      1. 1

                        …but ReasonML has way more semicolons than OCaml?

                        1. 1

                          I guess I’m full of it, I haven’t programmed in either just read some code and been influenced by facebook marketing, retracted.

                  2. 10

                    This was the biggest revelation for me

                    Do Python learners struggle less than C++ learners? We analyzed student submissions on small coding exercises in CS 1 courses at 20 different universities, 10 courses using Python, and 11 using C++. Each course used either the Python or C++ version of an online textbook from one publisher, each book having 100+ small coding exercises, expected to take 2-5 minutes each. We considered 11 exercises whose Python and C++ versions were nearly identical and that appeared in various chapters. We defined struggle rate for exercises, where struggle means a student spent excessive time or attempts on an exercise. Based on that rate, we found the learning for Python was not eased; in fact, Python students had significantly higher struggle rates than C++ students (26% vs. 13%)

                    1. 3

                      It kinda makes sense! The syntax of C++ is insane, but students will never encounter all of it, so it will not hurt them. It’s hard to know how much C++ students are helped by the compiler nudging them with warnings, but it could be a lot!

                      I know Python has warnings too, but I don’t think there are as many, and I don’t think people use them much.

                      1. 4

                        I wonder what the development environments looked like. Learning with a well-configured IDE is, IMO, an advantage because it literally tells you about many problems as you type. But I think there is a tendency to forgo an IDE with Python because it doesn’t really “need” one.

                        Also, I’d be interested in whether they used type hinting with Python. I used to hate writing Python but I’ve had to more recently and found that it’s a lot less annoying if I use type annotations consistently.

                        1. 4

                          one of the things that bothered me a lot when switching from java to python (and still bothers me a little, today) is that most functions never tell you the type of parameters they expect. will this be a string, or an object of some class?

                          so you waste a lot of time in TypeError-s and ValueError-s where as other languages (like java and c++) are explicit on what is expected.

                          also, even though I haven’t written java in almost 10 years, i still think that the javadoc is the best format for library documentation. packages-classes/interfaces-methods. you can browse it all, find what you need and discover a lot more.

                          python documentation… ugh. it’s an exercise in infinite scrolling and searching in the browser, over and over again.

                      2. 10

                        He makes some points about why Python mightn’t be the best choice but… SCALA? REALLY?

                        It’s clearly an amazingly capable language, but to quote Christian Beedgen “Scala is a VERY sharp tool.”.

                        I honestly don’t understand how a language with so many potential pitfalls for the unwary could be a good choice for a first language?

                        1. 2

                          I think it’s a great choice, but I agree it’s an imperfect razor sharp tool. What language would you choose instead?

                          1. 4

                            Thinking about this, this is the ULTIMATE bikeshed question, is it not? :)

                            So, let me first admit that I am poorly qualified to make that distinction. I am not a teacher and by most measures I’m not even a computer scientist.

                            I’m a sysadmin -> release engineer -> software developer who was raised by wolved cobbling together process oriented automations using shell, Perl, Ruby and Python as my skill set evolved, in that order :)

                            The thing I keep coming back to, and where I part ways with most better qualified folks on this topic is: I like abstraction, and I know for myself that my interest in programming only REALLY blossomed when I encountered languages that presented themselves to me at a high level of abstraction.

                            So, I can see where Scala could actually be nice, in that you could teach basic OOP which is a nice easy conceptual framework for students to absorb, but get into some of the more ascetic levitation and flying functional stuff later as they develop their chops.

                            As to what I would pick? I’d pick Python. I don’t personally feel stunted for life from learning other languages because of my preference for it, and I think that giving students a tool that will allow them to experience high velocity development is a great way to start.

                            1. 2

                              I agree that Scala is a sharp tool, but I think that’s the point. Those pitfalls typically show themselves right away in the form of syntax errors. When speaking of languages like Scala or Haskell, people often say: if it compiles, it’s probably correct. I definitely can see that feedback being valuable for teaching students. And because Haskell’s errors can be quite arcane, Scala seems like a better choice.

                              Anecdotally, my partner went to Trinity. The author of this post was her professor, and she learned Scala. Though she only took that one class, and hasn’t written any code in the years since, she still understands concepts like types, OO, and functional programming when I talk to her about them.

                              1. 1

                                You’re actually supporting my bikeshed argument :)

                                Given a superlative teacher you can derive tremendous value whatever the choice of language. Scala is probably an excellent choice for this person and their students for all the reasons you outlined.

                                The question is - for everyone who won’t have the benefit of Professor Amazing at Trinity, is Scala the right first language?

                                The answer may well be yes. I really don’t know.

                            2. 1

                              I don’t think the language per se makes any difference; what matters is the tooling, and the instructor. I did SICP at Chicago back in the dark ages, but aside from leaving me with a deep fondness for Lisps, the fact that it was Scheme and not, say, Pascal was almost irrelevant.

                          2. 8

                            def foo(a, b=[]):

                            Yeah, that is such a massive footgun in Python, and I can’t believe that it wasn’t fixed in Python 3. I thought Python was supposed to be “one way to do it”. How can it be that the obvious way in Python is invalidated by super technical distinctions like a difference between memory scope and binding scope?

                            I guess I am a radical FP enthusiast, but I see software getting slow not because we have too much immutability, but because leaky abstractions enforce massive overhead (like dockerizing everything).

                            So the big problem in software engineering is leaky abstractions! Isn’t it sad if you have to compromise the realistic but unpopular ideal in a CS1 course because of the way the industry turned out?

                            1. 4

                              I guess I am a radical FP enthusiast, but I see software getting slow not because we have too much immutability, but because leaky abstractions enforce massive overhead (like dockerizing everything).

                              I think the reason software is getting slow is because it’s cheaper to buy more/many powerful processors than to pay devs too plug those leaks.

                            2. 6

                              I think people here are forgetting that a very large number of people taking the Introductory CS courses are actually not CS students but people who will need to do some amount of programming within their own field. This is the reason why Python is a great choice, many tools and libraries for different fields use Python or have good libraries for Python and it will be the language those people will be writing in their future. Python is actually useful for them.

                              For CS students I agree that the language should be probably different. I’d either choose Racket or something “closer to the metal” depending on what kind of programmers you are trying to grow, maybe even JS/TS or Java.

                              Also, any programmer who wants to become actually a great programmer should learn at least a handful of languages and paradigms, the first language should be just enough to keep them interested and creating things during their first year or two of learning.

                              1. 4

                                Warning : Not a SE or CS by formation, I happen to code at work (and do it a lot) and self-learned even if I had to learn Matlab during my university days.

                                I found that Julia can be a better fit for the bill than Python for CS (or Scala) as an introduction to CS (maybe I am plainly wrong) but :

                                • Clear block syntax and scoping rules
                                • REPL-powered and scripting interface accessible
                                • Juno is a nice and modern environment to work with
                                • Complete in of type system and generics.
                                • OO-like approach available and easly understandable (struct and method defined out of the class)
                                • Access to C/Python/R libraries ecosystem in addition to Julia ones
                                • Coroutines, concurrent and parallel computing accessible
                                • Networking may be a rough edge for now
                                • Multiparadigm and can be a good bridge to a lot of different programming languages.

                                Maybe I am missing something in the core requirements that Julia can not fit ?

                                Having coding mostly in Python and R and SQL for work and playing (more like trying for fun without any project but being able to read code) on the side with C/Rust/Haskell and Julia. I have the feeling that it would have been the best language I could have been exposed first to. It seems, for me, to cover a lot of basic CS ground and the ability to build up knowledge in a lot of direction based on it.

                                1. 3

                                  At my school we used Miranda. A predecessor to Haskell.

                                  It was great at the job it didn’t, but we really should have used Haskell. The types were good.

                                  1. 2

                                    I’m the author of a book on intermediate/advanced Python programming, and the founder of a company that does professional Python training.

                                    I am in total agreement with this article.

                                    For a starting CS student, laying a solid foundation matters more than anything else at the start. Python is a light-saber chainsaw when it’s used by someone with that strong foundation, but makes a creaky foundation itself.

                                    Also, and this doesn’t matter as much, but I was delighted he concluded Scala was a great starting language. That’s what I would have picked too.

                                    (By the way, a large part of Python’s market is people who are not CS majors, but smart individuals who suddenly find they must do some coding as part of their job. The above doesn’t apply to them; the ROI of their time/energy is better leaping head-first into Python. It also doesn’t apply to type A data scientists, though it might apply to type B and probably to data engineers.)

                                    1. 1

                                      It also doesn’t apply to type A data scientists, though it might apply to type B and probably to data engineers.

                                      I am unfamiliar with Type A and Type B data scientists, where can I learn more about this concept?

                                      1. 2

                                        I believe the original distinction was made my Michael Hochseter:


                                        That Quora post of his inspired quite a few other articles - search for “type a type b data scientist” and you’ll have plenty to read.

                                    2. 0

                                      REPL and Scripting interface for CS1.

                                      No, no, no! A thousand times no! Remember that these are newbies. Just like you don’t let kids drink or smoke (even though these can be fun) until they are old enough, you also don’t let freshmen program in any way other than the disciplined one (think, only then code) until they have passed their first course in data structures and algorithms. The fun trial-and-error stuff can wait until they are mature enough to revert to disciplined mode when necessary.

                                      Static type checking and lots of syntax errors instead of runtime errors or logic errors.

                                      Static types are a good thing, but simplicity is more important than static types for newbies. What you want is, ideally, a gotcha-free language. Such a language is necessarily a toy one, but it can be obtained from a real one by removing features. For example, you could start with Standard ML and then

                                      • Remove modules and functors.
                                      • Remove exception handlers. (Raising exceptions is okay, but it will immediately terminate the program.)
                                      • Restrict type and exception declarations to the top level.
                                      • Restrict function definitions to the top level.

                                      And then maybe this has some hope to be simple enough for newbies. Similar proposals can be made that start with, say, Scheme or Pascal, and then remove features that newbies do not need to learn yet. But, if you start with a monstrosity like Scala, there will simply be to much to remove.

                                      Syntax/semantics allow coverage of things I care about including const/mutability, block scope, OO, subtyping, parametric types, etc.

                                      I agree that you want block scope, of course. But OO and subtyping? When choosing an introductory language, one must cast their personal preferences aside, and think of what is actually good for the students. Let them decide what technology stack they prefer (when the time comes), but for now teach them to think. What they need to learn is “How do I find the loop invariant?”, not “Should method argument types be covariant or contravariant?”

                                      Access to the full JVM for libraries.

                                      This is a non-argument.

                                      Expressive syntax that combined with libraries that allow me to give interesting assignments that don’t take thousands of lines to code.

                                      There are lots of interesting assignments for newbies that can be solved without using fancy libraries. Maybe you want to give them a simple GUI toolkit for their final project, but that’s about it.

                                      1. 4

                                        fun trial-and-error stuff can wait

                                        Trial-and-error is a great way of learning. If you don’t provide a REPL, people will still use trial and error, they just waste time wrapping the thing they want to try in boilerplate. I don’t think anyone who learns in a REPL is going to end up any less “disciplined” than someone who had to write out a full program before getting any feedback. They will, however, know lots of things that they just tried out of curiosity in the REPL, because they could.

                                        simplicity is more important than static types for newbies. What you want is, ideally, a gotcha-free language

                                        The ability to pass a value of the wrong type around for ages before eventually being told that it’s not a duck seems like a pretty big gotcha to me. I think it’s simplest for the user if you can keep the failure as close to the problem as possible, which is what static typing does. It also imposes that discipline you were so enthusiastic about one paragraph earlier.

                                        1. 2

                                          I don’t think anyone who learns in a REPL is going to end up any less “disciplined” than someone who had to write out a full program before getting any feedback. They will, however, know lots of things that they just tried out of curiosity in the REPL, because they could.

                                          An interactive environment can be used the way you suggest, to try new ideas and get immediate feedback about them. An example would be using the REPL to discover ways to generate ASCII art. This is unquestionably a good thing.

                                          But a REPL can also be used in a way that I consider less good for freshmen, namely, to allow themselves to think sloppily and let the REPL find the edge cases that their reasoning has not covered. This will get in the way when they have to learn data structures and algorithms, which is why I am saying REPLs have to wait until they have passed that course.

                                          The ability to pass a value of the wrong type around for ages before eventually being told that it’s not a duck seems like a pretty big gotcha to me.

                                          Freshmen are not going to manipulate super duper complicated data types. There is no shortage of algorithms that only manipulate arrays of ints that can keep them busy for a whole course. Types do at best a mediocre job of helping you convince yourself that such algorithms are correct.

                                          I think it’s simplest for the user if you can keep the failure as close to the problem as possible, which is what static typing does.

                                          It is what static typing sometimes does and sometimes doesn’t. Some form of static typing is probably a good thing, but a monstrosity like Scala, as the original article suggests, is definitely not the way to go.

                                          It also imposes that discipline you were so enthusiastic about one paragraph earlier.

                                          I’m not so sure about that. When I was in college, my favorite language was C++, and I was horribly undisciplined, jumping to implement the next generalized abstraction that popped in my mind, even though the current one I was working on was not even half completed. Even today, I rely too much on type inference to tie loose ends for me.

                                      2. 1

                                        Another alternative might be Kotlin. It has almost all of the benefits this article discusses (e.g. JVM compatibility, static typing and REPL support) without the complexity of the Scala type system. Kotlin also has a number of great educational tools for developing a CS curriculum.

                                        1. 1

                                          Note: This paper posted here provides some evidence for the OP’s concerns. Notably, previous languages does interfere with learning a new language.

                                          1. 1

                                            I saw two examples that basically boil down to “you are not supposed to do this, therefore the language should not permit you”.

                                            This is basically treating students as too stupid to learn not to do something, and therefore should be forced to not to something instead, by the language.

                                            Needless to say, that this attitude shows one’s disregard for other’s intelligence.

                                            The alternative is simply explaining why it’s bad. And if they still want to do it, well, they are allowed to learn the hard way.

                                            1. 1

                                              I’ve thought a lot about what the best way to start programming would be. It’s important to note that I am talking about practical programming more so than CS education, which is why this message is slightly off-topic, but hopefully interesting nevertheless.

                                              Based on my personal experience, when it comes to learning how to program, I think of languages on a sort of horseshoe spectrum. The horseshoe describes how much the abstraction provided by the language leaks through to the user.

                                              On one side of the horseshoe, there is assembly language, where very few abstraction leaks can ever happen, because assembly language is just about the lowest level of abstraction any programmer will ever work at. Assembly language is also, in practical terms, the closest you are ever going to get to the truth of “how the computer actually works”. Being exposed to this from the get-go, I posit, is a Good Thing.

                                              On the other side of the horseshoe, there are symbolic languages, for example LISPs. When programming in a LISP, it feels like I really only care about the abstract symbols, and the machine underneath completely disappears. This is also a Good Thing.

                                              From this perspective, every other language feels like it is stuck in an uncanny valley of abstraction level. Not low level enough to show you what is really going on, and not high level enough that you can just think about it without having to bother with irrelevant minutia. That is why I think learning how to program should happen by traversing this horseshoe from the extremes of “purity” down to the muddy, uncanny valley of “practical languages”. Preferably, students should advance on both fronts (from assembly language up, and from symbolic language down) simultaneously.

                                              1. 4

                                                This is a false dichotomy which ignores all the advances that have happened in type theory. I’m kinda bored being sick with high fever at home so I will provide you with a new (fever-delirium-induced) model:

                                                The landscape has a great big plain with all of the dynamic languages (python, lisps, perl, php, etc.), lets think of them as cities and we can order them by how much the runtime has to work to keep the leaky abstraction from collapsing (e.g. lua > ruby).

                                                Then there are some lakes and oceans where the merfolk live, sadly some of the merfolk have genetic instability causing some of their offspring to not be able to breathe underwater, these civilizations are the GC-free languages and the less safe / more UB the greater the genetic instability. Some of these mutated children manage to get to land before they drown and proceed to journey the land.

                                                Next we have the mountain ranges where the hermits live, these are the statically typed languages, at the roots of the mountains you will find languages like Java, then OCaml but the higher you climb the more pure the type system becomes.

                                                Finally we have the sky people / aliens, they have some contact with the mountain folks but there’s so few of them that they don’t really have much of a hierarchy, these are the logic programmers, here we have languages like mercury and prolog.

                                                The plains dwellers want to go to space and meet the aliens, just because why not? The plains people aren’t too fond of the hermits so they don’t know anything about the aliens, but they built this airplane called SQL which they use to catch glimpses of the sky people and travel between the cities.

                                                Now how does this landscape account for the sans-GC haskell-y languages that are on the horizon, like Neut (and others..)? Mountains in the sea? Portal on the top of the mountain? idk, these kinds of stories are often more harmful than not in actually providing any sort of explanation of things because all sorts of accidental structure shows up in the story for the sake of narrative.

                                                To be fair symbolic vs assembly has been a good lens for a while, but it breaks down when we have abstractions that are not leaky which describe computation adequately. This is active mathematics research and probably will be rather fringe for few more decades.

                                              2. 0

                                                Going back to using typed languages is a downgrade from Python if you don’t have knowledge about dependent typing.

                                                Also if your language enforces compile from scratch/run cycle, it sucks just like Python sucks when it does that, precisely because it’s interpreted but not properly typed language.