Everyone Disagrees With Me

Posted January 4, 2008

...except Hampton.

First of all, I want to thank everyone who replied to my last post with well-thought-out critiques and counter-arguments. Half the reason I write this blog in the first place is that I don’t have any idea what I’m talking about and this is a great way to get people to explain it to me.

You guys rock.

I also want to clarify what I was trying to say in my last post. I think there were a lot of misconceptions about that.

For instance, I wasn’t trying to say that Java was a good language to teach the entire computer science curriculum. Students absolutely need experience with all sorts of languages.

Functional languages, logic languages, that sort of thing… not only are they essential, chances are you can learn much more from writing a few programs in them than you could from an entire career writing java.

I also wasn’t trying to say that Java was the best language to learn first, by any means. I remember, in the second day1 of CS(Computer Science) 142 (the first class in the intro CS series at UW(University of Washington)), when the instructor put up the first Hello World program:

public class HelloWorld {
  public static void main(string[] args) {
    System.out.println("Hello, world!");
  }
}

He said, “For now, public class will be an incantation. So will public static void main(string[] args). They’re magic, they make your program work. We’ll talk about them later, but for now, just don’t worry about it.”

You really shouldn’t have to do that for an intro language.

All I was really saying was that Java was effective at teaching me to program. And, I guess, I was extending that to claim that Java is an effective first language.

Having read all the responses to my post, though, I’ve given it some more thought. I’d like to revise that claim. Now I think that there are aspects of Java that make for an effective first language.

One of these aspects I went over in a lot of detail in my last post: a lack of dependence on closures.

This is why I thought, and still think, that Ruby would make a pretty terrible first language. In order to do any sort of loop in anything like an elegant fashion, you’ve got to use blocks. Even the for-in loop is syntactic sugar for calling the each method with a block. And explicit blocks are all over the standard library.

I think I can generalize this aspect to something like a lack of dependence on advanced concepts, where “advanced concepts” are those for which you have to have a working understanding of some more basic concept in order to use the advanced concept.

Closures are an example of this. You need to understand how functions work before you can understand closures.

Another example is object-orientation. Object-orientation is a complex concept for a beginning programmer. The intro CS series at UW doesn’t attempt to tackle it until the second quarter. Java falls down here – it’s standard library is object-oriented to a fault.

So, thinking about this made me wonder: if Java’s not the best first language (and at this point I’m pretty convinced that it’s not), what is? What would the perfect intro language be?

It would have to make Hello World trivial, of course. No incantations. No classes or methods or functions wrapping it. Just calling a function on a string, at the top level.

It would have to make it easy to work gradually to more complex programs. Functions should be simple, straightforward, also defined at the top level.

The basic control structures should be basic. Conditionals are pretty easy to get right, but loops are more difficult. There should be a good way to iterate through an array or count up to a number.

Maybe a for-in loop is good enough for this, but I feel like those are sort of magical. At some level, everything’s an abstraction over something else, but it feels different saying that

for (int i = 0; i < 10; i++) {
  System.out.println(i);
}

works because for is a language construct and saying that

for i in 0..10
  puts i
end

works because of magic stuff about blocks and ranges that you haven’t learned yet.

The standard library for the perfect intro language would also have to have a few characteristics. Likely new learners will want to use the library, or subsets thereof, before they’ve learned some of the more advanced concepts. Thus, the standard library shouldn’t be deeply wedded to concepts like Ruby’s blocks or Java’s objects.

I’m really not familiar with that many languages, but I don’t think I’ve encountered any that exactly fit this bill. Maybe the reason for this is that such a language would suck for purposes other than education, and history has shown that students don’t like learning languages that are practically useless.

But there is one language that comes to my mind that is useful and fits at least some aspects of the above description: Python.

I’ve never actually done that much with Python, but I think two of the aspects that have made me a bit edgy towards it would actually be beneficial for learners.

For one thing, Python’s not deeply object-oriented, unlike Ruby or even Java. Much of the standard library is written in a very non-OO style. Python also doesn’t use functional features like closures nearly as heavily as, say, Ruby. It does have for-in, but that may be unavoidable.

Interestingly, Python is the language MIT has chosen to try for its intro CS courses. I hear there’s also an optional Python sub-course thing for this year’s intro CS students at UW. I’m interested to see how both of these go.

Before I wrap this up, there’s one more comment several people made about my last post that I’d like to address. Unfortunately, I don’t think I’m able to.

They brought up learning a functional language as a first language. This is an interesting alternative, but one so foreign to how I learned to program that I couldn’t begin to speculate how it would work out in comparison to learning a procedural language first.

If your first language was functional, I’d love to hear your take on this.

1 The first day of almost every course consists of “Here’s the syllabus, here’s the homework schedule, etc.” I hate that.

David W said January 04, 2008:

I like Scheme as a first language. You can teach the syntax in the first lecture and have time left over. Unfortunately it’s a little hard for beginners to actually get anything useful done with Scheme. Because of that I think Python is a good compromise. It’s reasonable simple and people can get started doing useful things with it right away.

Shalev said January 04, 2008:

Your key point is the concept that students should know exactly what they’re doing from the start, and then build upon the basic concepts to reach the more advanced ones. In my experience this leads to a very slow and painful style of learning.

I guarantee that someone new to programming will understand:

my_items.each do
  something
end

a lot better than

for(int i = 0; i < items.length; i++) {
  something(items[i]);
}

So what if the ruby version uses a closure? Most of the basic code in ruby uses closures implicitly and naturally. You don’t need to understand the detailed specific of closure to use them in the vast majority of your code.

The key advantage to top-down learning versus bottom-up is that it gives you a complete picture before the specifics are discussed. This way you can say things like “Remember how we were able to do something for each item in our list? Well that’s called a loop. Other languages aren’t as cool as ruby so instead of just saying ‘do something for each item’ you have to manually tell the computer how to access every single item in the list….”

At this point the student will already have an implicit understanding of the concept of iterating through a collection and how such iterations are useful in a program. The specifics of clunky syntaxes can then be explained as necessary.

Hampton said January 04, 2008:

Alright, I’ll explain my thoughts more.

I found that how strict and predictable Java was made me learn to think about my problems correctly. It forced me into thinking about strict objects and how they work together to build semantically named and logical components.

Moreover, finding Ruby was like a breath of fresh air. It allowed me around “walls” that Java had put up around me. I could do things I had the need to do easier. Like, learning in a tight, strict environment with very little magic, and then going into a magical land where anything is possible. The structure helped me learn good methods for when I had more freedom with Ruby.

Meh, 90% of programmers are terrible anyway.

Mike Woodhouse said January 07, 2008:

I think Python’s probably a pretty good starting point, although that unnecessary trailing ”:” annoys me. I introduced my kids (8 & 6) to Scratch as a fun way to start the idea of controlling the computer, but I’m undecided about what “real” language will be best when they’re older – I hope we’ll be able to go straight to Ruby but we’ll have to see.

At least they won’t be kicking off with mainframe BASIC on a 1970s machine with magnetic drum storage. Or moving on to Fortran and then COBOL, the lucky little blighters.

me22 said January 25, 2008:

I think the biggest problem here is that people are so used to Java-type languages that they consider thing like closures to be “advanced features”. If you’ve never learnt otherwise, it might seem perfectly logical that if you use a variable, the language will be smart enough to keep it around for you.

Personally, I like the idea of SML as an introductory language. Incredibly strongly typed, but without needing any incantations. It’s about as strict and predictable as you can get (though some type errors can get ugly). Not to mention that if it compiles, odds are good that it works, unless your logic is wrong.

It also saves the whole problem of value semantics versus reference semantics. When you can’t change something, it doesn’t matter whether you pass the telephone number or the whole house.

In many ways, it’s basic off even fewer concepts than Java. For introductory things, basically all you need is pattern matching, which is quite reasonable. For looping, all you can use is recursion, which works just like sequences definitions in math.

Math:
0! = 1
n! = n*(n-1)!
SML:
fun fact 0 = 1
  | fact n = n * fact(n-1)
As for the loop example:
fun print_range n m =
    if m > n then
        ()
    else
        (print n; print_range (n+1) m)

Side effects are icky :P

( But I still wouldn’t want to make a new programmer deal with Haskell and monads )

Make your comments snazzy with Textile!