Monday, April 11, 2011

New Languages Can Broaden Your Horizons

"A language that doesn't affect the way you think about programming is not worth knowing." -Alan PerlisIn linguistics there is theory called the Sapir–Whorf hypothesis which asserts that our language limits and defines our cognitive processes. In the computer realm Alan Perlis once said that, "A language that doesn't affect the way you think about programming is not worth knowing."  I don't know how true these statements are, but I do know that just about every programming language I have learned has taught me new ways to think about software development. In many cases I wasn't even aware of what I didn't know until the new language opened my eyes.

Here is an overview of languages that have changed my way of thinking about programming, in the order that I used them. I realize that some of the concepts that I learned in one language could've been learned in another, but this is the order I personally learned things. Sometimes just the act of doing something new can show you things that you could have done with the old tools, but hadn't considered in the past.

Languages

BASIC (in particular, Commodore 64 BASIC) - my first programming language. While much of what I learned at this time I later had to unlearn, learning that computers aren't magical but just do what the programmer tells them has had lifelong value.

Assembly/Machine Language - I learned what the computer actually did - i.e. what does high level code get turned into? What instructions can the CPU actually perform?

Pascal - with Pascal (well, technically Karel the Robot which we did for a month, first) I was introduced to the concepts of functions and procedures, as well as how to use looping constructs effectively. I also learned about pointers and data structures in Pascal. (i.e. the basics of structured programming as we know it). I was also first exposed to separating interfaces from implementations with Pascal (Turbo Pascal's Units), but it didn't really take.

C - C was a combination of Pascal and Machine Language for me. I still could use all of the structured programming concepts that I knew, but with C you could see how it tied to machine code. e.g. pointers are just memory addresses. They say that C is "A language which combines the flexibility of assembly language with the power of assembly language." For me this was its beauty. While not being quite at the Assembly level, I could understand exactly what my programs were doing. I also learned about the difference between compile time and link time, which is critical to understanding how to build large systems.

C++ - This was my first real exposure to OO. However, it didn't really click. I liked the encapsulation aspect of grouping methods with data in classes, but at this point in my programming life I still didn't "get" OO. What I mainly got from C++ was a way to structure my C code.

LISP - besides having Lots of Irritating Stupid Parenthesis, it introduced me to functional programming. I learned it is possible to program with variables that don't vary and without iterative constructs (Lisp has looping constructs, we just didn't use them till later on in the class). Very powerful ideas, though I have to admit that I didn't use LISP for long (even though I thought it was really cool), so I probably didn't absorb as much from it as I could've from the language.

Java - with Java I finally got OO. (or at least I think I get it now.  :) ). I think the key feature that helped me was the concept of an interface. Why would you want to create a "class" that doesn't do anything? It was the struggle to answer that question that turned on the light bulb in my head for getting OO. The other cool idea Java introduced me to was the idea of namespaces. While C++ was starting to get them at that time, I had never used them, and had been burned by class name collisions. Namespaces are a good idea for large programs, and a necessary one for large scale code reuse.

When Java 5 came out and annotations came into the language, this was another feature that didn't seem to have any value at first. I have since seen that there can be pretty large value in declarative programming though I don't think I have fully internalized how to use the power of it yet. (i.e. I can see the power of this idea, but it hasn't actually transformed me yet).

C# - when I first learned C#, it didn't change my way of thinking. It was just Java with slightly different syntax. But then I got a chance to play around with LINQ. There are two powerful ideas that I got out of LINQ. The first is directly using query language notation inside a "normal" programming language. The second is that of late evaluation. i.e. if you had something like
DataContext context = new DataContext(<db stuff>); 
Table<Person> people = context.getTable<Person>(); 
var query = people.Where(p => (p.age >= 20 && p.age <= 25))
                  .OrderBy(p => p.name).Select(p => p); 
foreach (var person in query) {
  Console.WriteLine("{0} is {1} years old", person.name, person.age) 
}
I would've naively implemented the getTable method to return the entire datatable, Where to return the reduced datatable, OrderBy would sort, and then Select would return an array with the values. This is obviously much less efficient then just letting the database do the query for you. One of the clever thing that the LINQ folks did is they accomplished this. Where, OrderBy and Select all return objects describing their actions, not the results of their actions. Its not until you try enumerating the elements in the query in the foreach loop that any data is actually retrieved or manipulated. At that point the code is able to take advantage of everything you specified to act efficiently. Compare that to the Unix command line:
find . -name "*" -print | grep "taxes" | sort -r
I am used to this type of environment where find does a calculation and passes data to grep which then passes data to sort. The idea of just passing metadata around and then doing the calculation at the end requires greater coordination between components, but when you have that the result is improved efficiency.

Ruby - with Ruby I am learning that the static typing I learned to love in Java might not be as important as I believed. Duck typing is a great idea. Even cooler are ruby blocks, i.e. closures. Being able to pass blocks of code around is HUGE and definitely a transformative idea. Prior to learning Ruby, I would occasionally run into situations where I was frustrated by Java's lack of function pointers. But now! Now I have a hard time writing code in Java, because I keep wanting to solve my problems using blocks.

Rails - while Rails is just a framework, and not truly a language, I feel it deserves its own entry. Rails leverages Ruby in such a way that it feels like you are using a domain specific language. Cool ideas I learned from Rails are convention over configuration and how to actually use the MVC pattern.

Javascript - from Javascript I learned that OO can be done in multiples ways. OO in Javascript is nothing like OO in C++/Java/C#. In fact, everything is just an associative array. The power of what you can do with such a simple idea, in conjunction with closures, is eye opening.

Conclusion
I am not a language expert, and as such there are many important features of the above languages that I omitted. The point of this post isn't what features are in what language. Rather it is a personal history showing how new languages expanded my horizons and abilities as a software developer.

Have you learned a new language lately?

3 comments:

Jessica said...

I like Python but need to get to know it more to say exactly why. Also, I glanced at Haskell (tryhaskell). Functional programming might take a while for me to grok though so I'm focusing on the two new languages (to me) first :)

Jennifer said...

"Getting" the idea of programming to an interface is key to capitalizing on the power of OO. Unfortunately, I'm not convinced this concept is adequately highlighted when teaching students OO as undergrads. When I was in school, I understood that I could write a shape superclass and circle and square subclasses, but then all we did was call some getters/setters in a simple program, which is not enough to internalize the concept. I don't think I fully appreciated this concept until after I had been working on my first project at my current job for a few months, where I realized that programming to an interface allowed certain key modules to remain unchanged even as the set of objects manipulated by these modules evolved and grew. Now, nearly 7 years later, this feature of OO has allowed my code to remain relatively flexible and maintainable in the face of constantly changing requirements. Still, I'd love to figure out how this concept can be adequately conveyed in the classroom...

Michael Haddox-Schatz said...

I really don't know Python, so I can't comment on it. For me, one of the interesting things about functional languages is that people who like them swear by them, but the only place that I know of one used in the "real" world is Elisp, used to configure Emacs. Why aren't functional languages used more?