Why I’m Not Fond of Teaching Untyped/Dynamic Languages

So today I thought I’d write down some of my thoughts on teaching programming to novices and why I’m not terribly fond of teaching an untyped language as a first programming language. Now a few disclaimers to prevent unduly hurt feelings. I’m not weighing in on the old flame wars on whether “static vs. dynamic is best”, I’m not saying that I don’t like [insert your favorite language], and I’m not going to be claiming that you can’t have really good classes based upon untyped languages. I also want to clarify why I say untyped here when some people say “dynamic”. To me, a typed language is one for whom typing rules are an integral part of the formation of terms in the language, i.e. a program in a typed language that doesn’t typecheck isn’t really a program in the language. Meanwhile, you can execute programs in javascript, any lisp, etc. without any typechecking. The program may not always terminate correctly, but that doesn’t mean it’s not a program in the language. This is why I say “typed language” vs. “untyped language” because, to me, the distinction isn’t whether or not there’s some notion of type information attached to terms but rather whether or not the language can be said to consist only of checked programs. I also don’t like calling this “static” vs. “dynamic” because you can have “dynamic” features such as type providers in typed languages.

Maybe that long disclaimer and digression about language wasn’t necessary, but I’ve seen too many battles over the meanings of all these words and I just wanted to set what I mean for the purposes of my writing. If you have your own definitions and preferred terminology that’s perfectly fine, just apply the appropriate linguistic transformation!

As I’ve previously written, I’ve had some experience lately teaching complete newcomers to programming the basics of programming with javascript. In the beginning of this course, we were teaching only pretty basic features of the language as well. We started off treating it like C with higher-order functions and syntactically-nice record types. The biggest technical problems that I thought the students were having were

  • Understanding control flow through an imperative program
  • Spotting errors
  • Knowing the format of objects that are arguments to callbacks used by library functions
  • Knowing the format of an object returned by a library function

I want to talk about each of these in turn.

Seeing the flow of data in an imperative program is its own skill. It truly is. Once you throw in lexical closures with mutable state, the whole thing can get really tricky. What I tried to teach students to do is write down the chunk of code they were trying to understand on paper along with all the variables that were in scope as columns in a table. Then the rows of the table were execution steps and they try evaluating the code by hand updating all the variables in scope with each step of execution. This works for small snippets of code to give them the basic idea of what’s happening, but it’s not tractable for anything more than a few lines and a few variables at a time. It also all goes out the window if the code is calling other functions because then they have to go an manually inspect what side-effects that code may have. Being able to look at code and understand what’s happening and its control flow and what side-effects are involved at every point is an important skill and one that programmers in industry do need to develop! On the other hand, I feel like it’s a bit much to ask someone who’s trying to wrap their head around the very basics of how code executes to be able to keep track of all of these things in their head. I think it’d be more helpful, in this regard, to teach them something like Idris with its effect system or perhaps Haskell’s monadic way of isolating effects.

Yes, I know, there are plenty of people who would say both of those are too complicated for a totally newcomer to learn but I don’t actually buy that. Programming in an imperative language, if you haven’t done it before, isn’t the most intuitive thing in the world. It’s a very different kind of algebra, so different that it doesn’t really look at all like an algebra at all anymore, so you really can’t leverage the experience students already have with math classes all their life. Things like monads have, I think, a reputation for being so difficult to understand because it’s a very different way of thinking if you’re used to imperative and untyped programming. You basically have to unlearn what you know about how to structure programs. In my experience, though, the other direction is easier. When you’ve learned how to think of programs in terms of explicit state and effects and a more functional control flow, you can see the constructs of imperative programming for what it is. I wish I had some kind of large scale experiment on this but, sadly, the NSF isn’t returning my calls for funding on starting my own university system. All I have is anecdata, but I’m fairly confident in it.

The next major thing that happens all the time when students are just starting programming is that they make lots of little mistakes. Hey, I make lots of little mistakes and most especially when I’m tired. Making mistakes is just a part of programming, really. I know that it’s a typical bullet point to argue that “Static Typing Prevents Mistakes” and, in a sense, I think that’s true. I also understand that when you’re an experienced programmer you know how to design unit tests and debug code in ways that, hey, maybe you personally don’t feel like a type system actually prevents that many errors. I totally believe you if you feel that way! We’re talking about absolute beginners though. When a beginner has a program not work right, especially if it doesn’t work right and doesn’t fail, there’s a whole lot of flailing and “what happened why is this happening”. They don’t have the skills yet to know how to debug, since it’s its own skill that’s pretty difficult. Part of this is the nature of javascript since it definitely tends to fail silently and throw out lots of undefineds when you make a mistake. My personal “favorite” being that if you make a typo when asking for a property you just get undefined rather than a failure. A typed language, even one that’s not that expressive in its type system, will catch these little mistakes. A more expressive type system can catch even more interesting errors in logic, but that’s not even the issue with beginning programmers. I’ll also provide my own counter-argument here that the error messages for expressive typed languages tend to not be beginner friendly. I remember being so confused by some of the error messages I’d get when I was learning Haskell only to realize it was a typo that led to getting a kind error. I’m not sure what the solution is other than for us to collectively make error messages nicer to understand, but I’m not even sure how much nice you can make them. I’d love to hear if there’s ideas on this front.

The next two on the list were basically the same thing: untyped objects are just big opaque messes when you’re learning a library. Basically, you have to pour over documentation in order to try and figure out what an object “really is”, what methods it supports, and what these methods do. The documention is even pretty good for things like Node and the basic types that are included in the language such as arrays, but at the same time if you’re writing request handling for a Node server the documentations only goes so far in telling you all the things you can do with the request object that comes in. Essentially, the interface isn’t clear just by looking at sample code or docs. Improved documentation can help with this tremendously but, honestly, I don’t have control over who writes the documentation for the libraries we’ll be using in a class and trying to write enough learning materials to bridge the gap is extremely time-consuming. Typed languages, though, are self-documenting in way. Not so much that you don’t neeed well-written documentation (and again there’s the issue of confusing error messages), but enough so that if you were to look at a library on Hackage you can probably get a basic idea of what each function does just by skimming the types. I think this is a big advantage when teaching beginners. The last thing I want is for anything they learn to be an “incantation”, a piece of code that they think they need to put in but they don’t know why. I want the why and how to be as obvious as possible. Having a clear interface to every function and method would go a long way to reducing the number of incantations. Again, I’m not saying that well-described interfaces are inherent to typed languages nor that you can’t have really confusing types in a typed language, but I’m talking about the reality of learning to use libraries in your first language where the world isn’t exactly full of good documentation.

So what do I want to teach as a first language to beginning programmers? I’m not really sure. At the moment, I lean towards something like Idris but at the same time I have no real experience with teaching Idris to new programmers so I’m not sure. At the very least, I think it would allow me to explain the fundamentals of programming and the underlying algebra behind it in a more straight-forward way that students could carry forward into the kinds of languages that they’d use as professional programmers.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s