A couple weeks ago I happened upon an article by Hacknot called “Invasion Of The Dynamic Language Weenies”. It starts out well enough, complaining that our industry is dominated by unsubstantiated claims (what else is new?). I’m going to use the pronoun “he”, because 1) I don’t know this person’s real name, and 2) it’s a safe bet that the person who wrote this is male. That’s just the reality of our industry today. He qualifies what he says by saying that this fever is invading the software development realm. I think it’s been there for a long time. What about Sun’s claims back in the mid-1990s that Java was “write once, run anywhere” technology? I can tell you from experience that was hype. Some people will tell you it still is.
At first he focuses on an article he read in the Feb. 2007 issue of IEEE Computer, called “Programmers Shift to Dynamic Languages“, by Linda Paulson. He goes through some quotes that seem based more on emotion than fact and says, “See? This is what I’m talking about” (I’m summarizing). He then launches off of a claim that is made in the article:
What really piqued me was the following quote from Stephen Deibel, chairman of the Python Software Foundation and CEO of Wingware, a company whose principal offering is Wing IDE — an IDE for Python development. In support of the claim that DLs make for more compact code, the author offers the following quote from Deibel:
Python would use 10 to 20 percent of the amount of code that Java or C++ would use to write the same application.
He spends most of the rest of the article expressing doubts and sneering about this claim. I didn’t like the tone, but I agree with the doubts. I think the claim is pretty extreme. Maybe Python in combination with some libraries that take care of a lot of stuff for the programmer, like Rails does for Ruby, does the trick. Like with O-O, programmers will probably gain more power out of the library they’re using than from their own code.
It’s amazing some of the stuff you can do with Rails. All you have to do is watch the “Creating a weblog in 15 minutes” screencast on the Rails site to see that. Try doing the same thing in Java or .Net and see if you can do it as fast and in as little code, to see what I mean. This isn’t to say that Java and .Net couldn’t get similar gains in app. development speed by taking the same approach Rails does. I think it’d be possible, but difficult to implement mostly because of the way Sun and Microsoft structured the mechanism of dynamically creating classes. It’s so verbose as to cause most programmers to not even venture into this territory.
Actually, I had trouble imagining how it might be true even within limited domains. Though I’ve principally used Java for the last 10 years or so, and C/C++ for five years preceding that, I have a basic familiarity with Python, having written a few utilities here and there with it. Reflecting on those experiences I could see no basis for such a startling claim to code brevity.
But it’s not only this claim made for Python that I find impossible to reconcile with my own experience and reason. I find many of the claims made for other DLs to be equally dubious. I have a basic knowledge of DLs such as Perl and Ruby, and have worked with PHP quite a bit. Reflecting on my experiences with these, I cannot find any support for the grandiose claims made by proponents of some of these languages either.
I was tweaked by Hacknot’s argument here, so I decided to do a case study using C#/.Net 1.x and Smalltalk. I cover a bit of that here, but I’ll deal with most of it in a later post. I’ll just say that in terms of lines of code written/generated, the differences were not as great as I anticipated. I was a bit surprised. Smalltalk still came out (slightly) ahead of C#. It was a simple example, the equivalent of a console application. Perhaps if the task was more complex Smalltalk’s power would’ve been more evident. In my opinion the Smalltalk code was clearer in expressing the process. It took less time to write as well. In any case, I was humbled. I had been taken in by the notion that DLs would dramatically reduce the lines of code one would have to write, just generally. I never bought the notion that it would be 80-90% smaller, but I thought a reduction of 25-50% would not be unreasonable.
To set up further argument, Hacknot gives his definition of dynamic vs. statically typed languages:
What Is A Dynamic Language?
There is no canonical definition of a Dynamic Language, but some generalizations are possible. A dynamic language generally possesses one or more of the following features:
- Dynamic typing – Variables need not be declared before use. Their type is not determined until runtime.
- Runtime code modification – Class hierarchies and other logical aspects of the code’s structure can be modified on the fly e.g. methods may be added to classes / objects.
- Interpretation – Source code is read at runtime, translated into machine code as it is read, then executed.
By contrast, static languages have the following general features:
- Static typing – Variable and parameter types must be explicitly assigned so that they are known at compile time.
- Fixed code structure – Classes and other code structures are immutable at runtime.
- Compilation – Code is compiled into some intermediate form, often machine code, which is later executed.
Static languages include C, C++ and Java.
I’ve heard about others equating dynamic languages to “scripting languages”. To me this never seemed justified. One would be hard pressed to find anyone familiar with Lisp or Scheme calling them “scripting languages”. In my experience scripting languages are task-oriented. They’re designed to help you glue processes together to create a new process. Some examples are Bourne Shell/BASH, and AWK. I would even include Perl in this list, because it first became popular among Unix system administrators as a more powerful scripting language (more powerful than writing a script in a shell). When web apps. became popular Perl got brought into that. From what I hear it’s still a so-called “write-only” language. You can write a program in it, but it’s difficult to go back and modify it later. It’s been that way for years. Scripting languages are generally like this. Trying to read and understand a shell script can be a real trip.
Ruby, as an example, can be used as a scripting language, but it more closely resembles a language that would be used for building applications. I can’t speak for Python too much because I haven’t used it.
He is correct about his 3 properties of dynamic languages, but he leaves out some things. Here’s a good article on dynamic language properties. Dynamic languages often have:
- Late binding, meaning that an object is not bound to other objects until it’s called for at run time. This allows code to be modified while a program is running, and it’ll still work. If you’ve used “edit and continue” within Visual Studio, this is emulating what I’m talking about here, within the context of an early-bound environment. Essentially what ”edit and continue” does is recompile the changed module, and literally patches the updated binary code/bytecode into memory. Not easy to do. Even so, Microsoft developers missed this feature when Visual Studio.Net was first introduced, because it wasn’t there. It was re-introduced with VS 2005. Late-bound languages accomplish the same thing naturally, though I imagine in a different way. They’re designed for this.
- An Eval() function, which allows code to be compiled/interpreted dynamically at runtime. This may be incidental code that needs to be formed dynamically, executed for a particular function, and is then thrown away. It can also be used to create a script interpreter environment within a running application.
- Higher-order functions. This is a technical name for a function which takes one or more functions as argument(s), and/or returns a function. It’s helpful to think of this in mathematical terms. For example, a derivative in Calculus takes a function and produces another function. This doesn’t mean it has to be used in a mathematical context. I am saying that a background in mathematics helps in understanding this concept.
- Modification of code/code generation at runtime. This allows a program to modify a code library, or itself, to suit its needs. Lisp is well known for this.
- Closures, also known as Lambda expressions, or anonymous methods (recently added to .Net). They are blocks of code that basically act like a named function, but have no name. A program merely carries references to them for as long as they are needed. A key characteristic is like normal functions they are lexically scoped, which means that they acquire the scope where they are defined, no matter where they are ultimately invoked. They are often used to carry out an incidental step in a program. They come in handy, because they substitute for rarely used class methods. They are constructed or used at runtime by higher-order functions.
- Functional programming. Some dynamic languages have functional programming features. This means that the value of a variable, or the state of a program is always represented by a function, and a variable can only be assigned to once. (h/t to Giles Bowkett for the link)
- Continuations. These allow a program to access, and even change its own state at will. This can mean persisting the call stack and restoring it to its former state later, or it could mean changing where the return value of a function actually goes (ie. you can have it go somewhere else besides the function’s caller).
- Introspection, also known as reflection. This allows a program to look at an object and figure out at runtime what type it is, what methods it has, what the parameters are to those methods, etc. Java and .Net have this as well.
- Macros. This is a meta-programming construct which allows the programmer to add new features to, or change the syntax in a dynamic language. Lisp is known for this feature. This is especially useful in creating embedded domain-specific languages (DSLs).
Giving Hacknot the benefit of the doubt, he was trying to come up with a list that was common to all dynamic languages. With the exception of Visual Basic, most other dynamic languages have some of the features I list above.
So what do these features get you? Here are some that I can think of:
- Modification of code/code generation at runtime – An example of this is what the Rails framework does, in the Ruby language. Based on metadata in the database it dynamically creates the code for the object-relational mapping (ORM) model, which the programmer can access from his or her application code, and it creates code for what’s called a “scaffold” of the web client interface.
- Continuations - An example of where this becomes really powerful is the Seaside web framework for Squeak, and exception handling in CLOS (Common Lisp Object System) (h/t to Giles Bowkett).
- Closures – A good example of what this gains you can be seen in the following Smalltalk code:
| list output1 output2 |
list := #('Jim' 'John' 'Mike' 'Matt' 'Cindy' 'Melinda' 'Sally').
output1 := list collect: [:each | 'student: ', each].
output2 := list select: [:each | (each at: 1) = $M].
output1 will be all of the names with “student: ” prepended to them: #(‘student: Jim’ ‘student: John’ ‘student: Mike’ ‘student: Matt’ ‘student: Cindy’ ‘student: Melinda’ ‘student: Sally’).
The select method does a test, in this case it tests whether each name starts with the letter M. output 2 will be #(‘Mike’ ‘Matt’ ‘Melinda’).
Both collect: and select: methods are higher-order functions that form new collections out of what’s in “list”, by iterating over it and sending each element to the closure that was sent to the method. The closure is in ‘s. Each closure takes parameter(s) (the “:each” part) and produces output (whatever the expression in ‘s evaluates to). The pipe (‘|’) separates the parameters from the code. Each time the closure is called by collect:, it returns “student: <student name>”, and collect: adds that to a new collection it’s made. Each time the closure is called by select:, it tests the value. If the closure returns true, that entry is added to the new collection select: has made. If it returns false, it does not get added. When collect: has iterated over all the elements of “list”, it returns its new collection. The same goes for select:.
The actions carried out by collect: and select: could’ve been carried out using a foreach loop in .Net or Java, but once you understand the principles involved, this approach is simpler.
Hacknot is largely correct about his feature list for static languages, but I’d call them characteristics of an early-bound language instead, since I’ve read that Python has static typing features, as well as dynamic types. I’d take issue with his characterization that static languages have a fixed code structure. In large part this is true, but in Java and .Net it’s possible to construct classes at runtime, and instantiate them, though this is very cumbersome. The other features I would add to the list Hacknot gave are:
- Early binding. All modules that make up a program are bound together into an executable at compile time. This executable image generally cannot be modified except by recompiling the program. Hacknot correctly points out that this doesn’t mean that the entire body of source code has to be recompiled each time. The compilation step may only need to compile one module, which can then be integrated into the executable. There are other ways of speeding this up. Microsoft’s compilers have incremental compilation, for example, where it tries to only recompile what’s been changed inside a module, to make the process go faster. What this usually means is that in order for the program to be modified, it must be stopped, so that it can be recompiled, and then reloaded into memory for execution. In this case it must always start from a predetermined entry point in the program. ASP.Net somehow gets around this, probably by caching the JITted version of modules.
- Templates/generics, also known as parameterized types. This is a feature that was created in O-O languages to try to get around a couple of the inconveniences of static typing: manually creating repetitive code, or just taking Object as input and/or returning Object. They make it possible to create a class, or automatically generate a new function at compile time, where the programmer tells the class or function what type(s) to use within itself. This technically creates repetitive code, but the programmer doesn’t have to deal with it. He or she can just deal with the code template. This preserves the feature of static typing, because once the new class or function is created the types within it are fixed. The way they’re typically used are as a way of automating type casts.
Hacknot argues that static typing is like documentation for your “intent”:
Consider that when you remove type qualification from the source code, you’re also removing some documentation of your intent. . . . Consider also — if it’s good form to give your identifiers meaningful names to aid in comprehension, isn’t it equally good form to explicitly assign them types, for exactly the same reason?
Hacknot touches on this later, but I’ll address it here. He likes static typing because it’s something that can be caught by the compiler. This also means that all types are enforced. It’s binding. It’s not like a variable name where its meaning, as far as its type, is flexible. Even in strongly typed languages programming teams I worked with often named variables like “iCount” and “strItem”, simply because we didn’t want to bother to go looking in the code for where a variable was declared, and what type it was. We wanted to just get it from the name. This began to fade once I got into .Net programming, but I still used this technique from time to time.
As far as intent goes, let’s take a look at some code. This is from a real project I did as a test case between C# and Smalltalk, which I’ll discuss more in another post. I’m using a Hashtable, which contains arrays as values in the key-value pairs (this is .Net 1.x code):
public static bool ValidateClassForDepartment(
return (Array.IndexOf(((Employee.zClass )
employeeClass) != -1);
By the way, just at first glance, is my intent clear here? What am I doing? Modern business apps. use collections all the time. They’re essential to doing any serious development. But why would I have to do this, if strong, static types are supposed to help someone see exactly what my intent is? In fact a lot of functions within .Net and Java have been like this (for .Net up until Version 2.0). They input and return objects of type Object, because the designers wanted them to be generic. There was no way they could anticipate all of the different types that programmers would want to use and receive. Object is at the top of the class hierarchy, so it’s a safe bet. But what does this do for the programmer? Not much. Not even a compiler could catch a type mismatch in this case. The error would not be caught until the program was run. Even in the days when the C language was popular, library functions often took parameters or returned a value of type void *, because there was no way to anticipate what a caller would pass in, or what type would be returned.
Say I was unfamiliar with this collection, but I knew I needed to fetch values out of it. I wouldn’t know how the values were put in there, because I hadn’t looked at that code yet. So I fetch out Objects. What can I do with them? There a couple ways I could figure this out. I could go to the source code where these objects were inserted, and see what type they are. Or, I could run the code in a debugger and inspect what types I’m getting. How is this different from using a dynamic language? It’s not. I would use the same techniques to figure out what type(s) are in the collection.
In the case of the .Net code I am either forced to type cast what I get out of Hashtable to the type I need, or I suppose I could’ve subclassed Hashtable, and created my own type-specific accessors. Do I like extra overhead? No! This is the downside of static typing–inflexibility in library functions. Let’s take a look at the same functionality in Smalltalk, a dynamic language, using a Dictionary container (the equivalent of Hashtable in .Net):
^(deptClassAssociations at: deptSymbol)
I don’t know about you, but this looks clearer to me; less cruft. The one part that’s probably unclear to those unfamiliar with Smalltalk is that the “includes:” method is doing a test of whether the array includes empClassSymbol. I was able to get this clarity because of the dynamic nature of the language. I don’t have to type cast because all variables are just references to objects. The references pay no attention to the type they’re referencing. I can put an array into a Dictionary and get that same array back out when I access it.
Hacknot asks a question here, but then backs away from exploring the answer:
What about Python’s other dynamic feature, runtime code modification. Can it result in a reduction in code volume of 80 to 90 percent? I find this impossible to assess, as I have insufficient familiarity with self-modifying code. But I might mention that this lack of experience is quite intentional. Even when I find myself using a language that permits runtime code modification, it is a feature I am loathe to use for it seems to me to hold great potential for producing a code base that is hard to understand, and therefore difficult to debug and maintain. Additionally, there is significant cognitive complexity that goes along with dynamically modifying code, not unlike the intrinsic difficulty that accompanies multi-threaded code. In my view, this level of complexity is to be avoided if possible.
I think all he’d have to do is take a look at a framework that generates its own code at runtime, like Rails, and then see how little code the developer has to write to see that perhaps runtime code generation/modification is a good thing.
He equates “runtime code modification” to “self-modifying code”, and misses the point. He then says, “It’s too scary, so I won’t go there.” This is just my opinion, but I think it’s this sort of attitude which eventually leads to permanent unemployment among developers like him. The fact that he thinks multithreading is scary as well, and should therefore be avoided, only reinforces this point. Multithreading is increasingly becoming an issue that developers must deal with, because CPU cores are reaching a speed limit. The only way program speed can be increased is by multithreading. It’s just a matter of how it will be achieved. I personally don’t employ multithreading at the drop of a hat either, but I use it where I think it’s necessary.
Self-modifying code sounds scary to a lot of folks, but think of it this way. A lot of .Net and Java programmers are increasingly writing parts of their programs in XML as a metalanguage. They run the XML through code generators to get their .Net or Java code pieces, using it to generate code that they’re probably not going to look at. They just want the functionality out of it. Now imagine if instead of using XML they used .Net or Java code to generate code in their respective languages. This is essentially what “self-modifying code” is: generating code using the same language you are generating code for; using Java to generate Java code, or using .Net to generate .Net code. It doesn’t have to be scary. Where I think it gets into “weird” territory for most people is that most dynamic languages put the programmer into a constantly “active” environment, like Lisp, Smalltalk, or Ruby. In these cases, there’s little distinction between a program being in an active or inactive state, since it’s possible to modify a program while it’s running. This is unlike the traditional development model of writing code when a program is inactive (non-existent in memory), compiling the code, and then activating the program.
It’s possible to generate Java code in Java and .Net code in .Net, but it’s a verbose, laborious process. Some have ventured into that territory, but most have not. The reason “self-modifying code” becomes a feature in dynamic languages is the fact that it’s easier to do.
Hacknot asks some good questions here, but the answer may be that the developers he’s complaining about accept some things, which I’ve covered above, as a given. He doesn’t seem to understand what their basis for judgement is:
To reiterate, the abbreviation that brings ostensible benefit to the original author of the code may be an impediment to other’s comprehension of the code. Further, how do we know that the presence of compile-time type checking is not a nasty inconvenience, but an efficient eliminator of even nastier runtime errors caused by unanticipated type coercions? Without a detailed assessment of the negative effects of dynamic typing, how can we possibly determine its net effect on development speed? It seems to me that we can’t. But it also seems that many DL enthusiasts are willing to ignore such inconvenient issues, even when directly confronted by them.
For instance, I notice that in “Programming Ruby”, at the top of Dave Thomas’ list of techniques for debugging Ruby problems is “Run your scripts with warnings enabled” — in other words, examine the sort of information that a compiler would already have given you, were you using a static language! This tendency to exaggerate the advantages of some language feature while deliberately ignoring the disadvantages of that same feature is characteristic of much DL advocacy.
I thought this quote was interesting:
Indeed, the gap between what I wrote and what I intended is where so many bugs lie, that I have come to appreciate the value of being as precise as possible in specifying my intent. Therefore I use Design By Contract, because each contract is one more opportunity for me to spell out to myself, the compiler, and future maintainers, exactly what I intend and what assumptions I’m making.
An awareness of my own limitations is the most significant factor in determining my approach to software development as a whole. That’s why the dynamic language features that a weenie would call “flexible” I would call “an opportunity to err”.
“The gap between what I wrote and what I intended is where so many bugs lie”. I think this gets to the heart of why I like dynamic languages. The intent shines through more. It’s not burdened with cruft, which obscures the intent of the process I’m trying to express, which it sounds to me is what he’s talking about here. Design by Contract is not a bad idea, but this is not something that is achieved by merely using a statically typed language. It helps, but often more code needs to be added to insure that inputs and outputs meet requirements and assumptions. In a dynamic language a little more code would have to be added than in a static language to insure types were correct, and to produce an intelligable error message if they weren’t.
A proposal in a paper I found while writing this might make things easier on everybody. It argues that future languages should contain both static and dynamic typing features, so developers don’t have to make a black or white choice. And indeed, such languages are appearing. As I said earlier, I believe Python is one of them. Groovy is another. Whenever Microsoft comes out with their Orcas release of .Net, it will be another answer to this.
I thought his statement about “understanding my limitations” was interesting. He’s basically saying, “This is my skill level and I’m sticking to it,” which is fine. If he wants to stay where he is, he has every right to stay there. What I’m wondering about is why he’s so stuck up about other people making other decisions. He does say later that he really dislikes the emotionalism that is encouraged within our industry, which is a valid criticism, but he also really disses dynamic languages, as if they have no worth whatsoever. I think such judgements coming from him are really unjustified. He comes at dynamic languages from the standpoint of someone who’s uncomfortable with them to begin with.
Here’s a series of doubts Hacknot has about the claims made for dynamic languages. They’re not really based on anything, but for what it’s worth, it raises questions that should be answered:
Most DLs are interpreted rather than compiled. This means that your test/debug cycle no longer includes an explicit “compile” or “link” step which, DL weenies claim, makes for a “development cycle [that] is dramatically shorter than that of traditional tools.” Maybe so, but how significant an impact does that have on overall development speed? You don’t have to wait for the compile/link to occur, but you still have to wait for the thing to execute so you can test it, and interpreted languages are often slower to execute than compiled ones. So how much do you really gain overall? And there are other confounding factors. Perhaps the compile-time type checking of a static language serves to catch bugs that a DL won’t pick up until runtime, in which case some of your test/debug cycles for a DL will be spent catching type-related problems that a static language would’ve detected before execution began. I say “perhaps” because I just don’t know — I’m only speculating. I don’t have the data and so I can only guess as to what the net effect is on development speed. The problem is, the dynamic language weenies don’t appear to have the data either; but somehow they’ve attained surety in its absence. Again we see the advantages of a feature (interpretation) being extolled but its disadvantages being ignored.
Like I was saying earlier, modern development has often demanded things of developers that belie what Hacknot is talking about with respect to the advantages of statically typed languages. There have been plenty of cases where static typing doesn’t gain you anything, because the developer had to make a “generic” choice.
I think he’s totally off base in saying that debugging is slowed down because “you have to wait for the program to execute.” I’ve written some Ruby on Rails code on my 1 Ghz laptop. Granted it was for a simple app., but it ran plenty quick. For an interpreted language it ran fast.
Hacknot complains that the “dynamic language weenies” haven’t provided any data to back up their claims “but somehow they’ve attained surety”. I think it’s a matter of these developers having attained a sufficient level of experience with static languages that they assume everyone else has come to the same conclusions as they have about them. Obviously they were wrong, as Hacknot is a perfect case in point.
Many language comparisons performed by DL enthusiasts focus on concision as if it were the sole measure of programmatic worth. However there are many aspects of a code base that contribute to an overall assessment of its condition, volume being only one of them. Structure, commenting, consistency, naming, performance and maintainability all influence the efficiency with which programmers can work on a body of code. I would say the primary influencers are the qualities of the programmers themselves.
I think this is true for the most part, except that experienced developers tend to like technologies that support them to the best of their abilities. A weak language can waste a developer’s time. This is something he doesn’t cover at all: the powerful features in DLs. I don’t know if it was deliberate, but in effect he’s set up a strawman by saying the “weenies” claim that all of the advantages flow from them not having to deal with types in their application code. I certainly wouldn’t argue that. There are some advantages that flow from this, transparency in process being one of them, but not major ones, in my opinion. I personally like concise code. It’s easier for me to read. The less cruft I have to wade through the better. Granted it’s really helpful if the developer puts clear names on things, and structures the program well. Obfuscated code is never a good thing, in my opinion. And static typing doesn’t help with this. A process that’s written in obfuscated code is going to be nearly as bad in a static language as in a dynamic one. Yes, in a static language there is more information, but it’s not going to give you the answer as to what the intended process is. I’ve seen code written in a statically typed language written by a programmer who only used single-character variable names for everything. His process was unintelligable. I could pick out parts of it, but I could never get the whole thing without going to him and asking, “What were you trying to do here?”
I’d liken strong typing to training wheels, of a sort. Not that developers don’t need it for a significant part of their career, but after a point strong typing can feel like a straightjacket. It gets in the way, rather than supporting you.
One of the least influential factors influencing development speed is the amount of typing you have to do. We have IDEs with numerous keystroke-saving mechanisms built in such as smart editors, keyword expansion, templates, macros and so on. If code volume were significant as an influencer of overall productivity, then we would expect touch typists to be at a significant advantage to the hunt-and-peck typists which are so common in our occupation.
Yet there is no shortage of syntax-obsessed language comparisons of the form:
Here’s a toy task implemented in SSL (some static language). Observe that it takes 20 lines of code to implement. Oh, the humanity! Now here’s the same task implemented in SDL (some dynamic language). It only takes 10 lines. Therefore you’re twice as productive in SDL as in SSL.
I guess what he’d really like to look at is a full-fledged project with some real meat on it. Obviously it’s hard to do that in discussion forums. People don’t want to look at reams of code just to see someone make a point. I think what he’s really expressing here is he wishes there were more formal studies done, which is a perfectly reasonable request.
I can only speak for myself. There have been many cases where I have had ideas that I’ve abandoned because I felt as though there was too much work involved. It was the amount of code I’d have to write and the machinations I’d have to go through to get it done. If it could’ve been expressed more simply, I would’ve been inclined to go ahead with it. To say that the amount of code you have to write doesn’t matter I think just misses the mark. I think I should only have to write as much code as necessary to adequately express my intended process. I prefer using formal logic for this. It’s closer to what I’m actually thinking. I’d rather not be concerned with how the computer is going to execute it. In my opinion, most static languages force the programmer to think in terms of how the computer is going to execute your process. They take the focus off the process, and put it on the mechanism that executes the process. I’m using “mechanism that executes the process” as a metaphore for, as Ramon Leon often says, ”the work that programmers do to help the compiler do its job” of translating the source code into object code. I’d suggest that this is the reason Hacknot said earlier that so many of his bugs come from what he wrote being different from what he intended. He got distracted by the mechanism and was not focused on the process he intended to write.
Regarding the arguments made that dynamic languages are gaining legitimacy because of corporate backing:
Let’s not be so naive. Corporations are out to make money. They are not interested in advancing the software development field, championing an ideology or improving your productivity, except in so far as those activities will yield some financial return. If a company appears to be hopping upon your favorite DL’s bandwagon, they are simply acknowledging “There is money to be made here.” Commercial exploitation is not a hallmark of engineering worth. It will be in many corporations best interests to actively fuel the hype surrounding a DL in order to generate sales of their own related products and services. Hence the advent of the execrable job title “Technology Evangelist”.
When the dynamic language hysteria dries up — and it surely will — these same companies will eagerly pursue the very next trend to surface, just as soon as they can find something to sell to the niche market it has created. We’ve seen this same pattern so many times throughout the history of this industry, its reoccurrence is utterly predictable. Recall that when OO first came to the fore, companies everywhere were suddenly remarketing their existing fare as being object oriented.
He’s kind of beating a dead horse here. C++ and Java, the two languages he’s used most would not be the standards in the industry that they are without the hype he complains about. In fact, I’d venture to guess he wouldn’t have been using these languages, but some older ones.
Some of what he says is true here, but I think dynamic languages are here to stay. Groovy has support by Sun for Java. Microsoft, though they haven’t come out with it yet, has been moving forward with adding dynamic language features to .Net for the past two years, and is set to release these modifications next year. In fact these DL features are necessary for some of what Microsoft wants to accomplish with .Net.
The present IEEE article concluded with a statement from Carl Howe, principal analyst for Blackfriars Communications, a market research firm. He is also apparently a “longtime Unix coder”. He claims:
Languages are tools for programmers. As the problems change, people use new languages.
I suggest that of all the factors driving the current interest in DLs, a changing problem set is about the least important.
Even in the wider development community, the need to solve new problems is rarely to be found as a force driving adoption. Howe seems to be suggesting that adoption of a new language occurs when a developer finds themselves facing a problem that is not addressed well by existing languages, forcing them to search further a field for something more suitable. I suggest that the exact opposite is generally true. Enthusiasts start with the desired solution in mind — the new toy they want to play with — and go looking for a problem to apply it to.
I think this is where Hacknot starts coming off the tracks. What attracted me to Ruby on Rails, and then to Squeak and Seaside was the fact that I had been working on web applications for the past year-and-a-half, and I had been using ASP.Net 1.x. My experience with ASP.Net began well, because it provided a good training ground for learning how web applications work, but over time I became disturbed by some real shortcomings I saw in it. There were situations I had reached where the development tool I was using, Visual Studio.Net, was of no help at all. The page model that the ASP.Net API insisted I use really caused problems for me on one project, where I had to deal with taking submits from sections of a page, not the whole page at once, and updating multiple sections of the display as a result. I got it done, but at the end I was ashamed of the code I wrote. I kept up the code as best I could, but it was basically a mess. What I kept hearing from others is that even with these shortcomings, ASP.Net was still better than the Java equivalent in terms of the amount of code one had to write to get the same thing done. This was important to me because I had tight deadlines. C# and Java have been weak languages (VB.Net fairs a bit better, but not much), which are gradually getting stronger with time. The reason they can seem reasonable for many tasks is because of the tools, APIs, and components that support them.
I was attracted to Ruby on Rails because right off the bat I could see it solved problems much more efficiently than ASP.Net 1.x could’ve dreamed of doing. There are qualities to the Ruby language that enable it to do what it does well, which while possible in an environment like .Net and Java they’re more difficult to achieve. Groovy, based on the JVM, has its Grails framework (which is similar to Rails). Work is being done as I write this to create a Ruby.Net compiler. I think it will be achieved one of these days.
There were some aspects of RoR that puzzled me though. I’ve later come to know that the reason for the confusion was that the RoR version I was looking at used associations to simulate the named parameter syntax in Smalltalk. In fact there were many similarities I could see between Ruby and Smalltalk.
I was attracted to Seaside on Squeak (a modern version of Smalltalk), because it enables the web developer to write code as though they’re working on a desktop application, which is really what I wanted all along, from the time I first started writing web apps. State is maintained by the framework using continuations, alleviating the developer from having to think about it. .Net and Java simply cannot do this, not without a ton of work done on them, with significant costs in performance. Seaside does not have this impediment. Further, it uses a component model for building web pages. So you can have multiple independent components on a page that each receive their own submits, and/or update their own section of the page.
The statelessness of the web demanded new solutions to the problem, because the older languages were inadequate for the job, at least in the way they were being used. The older languages were fine for the GUI and console app. era. They could get the job done reasonably well, though I’m sure dynamic languages could’ve shined even then if the hardware speeds had been fast enough. The real problem I’ve seen with the web is that the technology behind it has been one kludge after another. Giving credit where I think it’s due, ASP.Net was a step in the right direction, but it’s a far cry from RoR and Seaside on Squeak.
One of the more desperate claims weenies are prone to making is that their DL of choice has a syntax that is somehow more natural, expressive or intuitive than those of other languages. Sometimes they’ll go even further, claiming that the DL syntax is so intuitive that it enables some sort of direct mind-CPU connection.
I don’t think I’ve ever claimed that the native syntax of a dynamic language was more “natural”. This can come close to being achieved using domain-specific languages in a DL. I have used the term “expressive”, but I think that’s accurate. If you compare between code written in a DL vs. an SL, you’ll see that the intended process tends to jump out at you more in a DL than in the equivalent SL code. As for the “direct mind-CPU connection”, well, I think that’s way off. The CPU doesn’t even enter into it. That’s what I’m trying to get away from, in fact.
I think where the “natural” adjective comes from is there are programmers who have a background in mathematics, like myself, who see what can be done in terms of syntax, and they really like it, because it matches with how they like to translate problems into a codified form, whether it be in mathematical terms, or in programming language code. “The language of math” is one that’s often tripped me up, but as a hobby I occasionally venture into those waters to learn more about it. The syntax of Smalltalk feels good to me though (there I go with the emotions…).
I’m using these rather well chosen examples to illustrate what should be self-evident — all programming languages are arbitrary, artificial constructs which must be learnt. They cannot be intuited. Further, what constitutes a “natural” syntax is infinite in scope and varies dramatically between individuals depending on what languages they have been exposed to in the past, amongst other factors. The language designer chose a syntactic option that was most natural to him at the time.
This is true. Every programmer who uses a programming language written by someone else is adapting ideas to the model of that language. Some languages are better at accomplishing some tasks than others. They are not all created equal, despite the claims about “all languages being Turing Complete”. Ah yes, but the question is how much effort do you have to go through to get something done. Yes, you could implement continuations in Java, but how much effort would you have to put into that, and would it even be worth it?
The rest of Hacknot’s post is not really worth commenting on. I don’t like arguing with people who froth at the mouth. He dismisses dynamic languages as the mere whimsy of fanatics, and blames the advocacy behind them for the sorry state of our industry.
The blame is misplaced. The reason for the emotionalism is partly because the people who choose to stay in this business are generally the people who are passionate about what they do. They’re the ones who survive in it. Being in the technology business is tough. Another part of it is some people know who the true buyers of technology are, and they’re not us developers. They’re people who, in the least, hopefully have a bit of a clue about technology, because that’s the most we can expect of them right now. If you’re an experienced developer you know who I’m talking about. They are guided by salesmanship, not technological merit. One reason technologists resort to hype is because they know this. If languages were never hyped, we’d still be programming in assembly language today, or maybe Cobol and Fortran. Frankly I’m thankful we’re not.
I’ll give my own opinion in response. I understand his frustration. I used to get frustrated with the claims that open source (basically, read “Linux” into that) is inherently ”secure”, “never crashes”, and “will take over the desktop from Windows”. Time has borne out that these claims are not true as absolutes. It certainly doesn’t mean that open source software is bad or inadequate to solving real problems. I’ve never liked it when advocates make something out to be more than it is. There are instances, I’ve discovered in the last year, where open source software is really good, and ahead of anything else out there. It’s actually innovative. I think some of that breathlessness Hacknot complains about is people acknowledging this innovation.
Secondly, dynamic languages were not just invented by “corporations who are out to exploit the gullible”. They were invented by some serious academics. A few I can think of are Dr. John McCarthy who invented Lisp, Dr. Gerald Sussman and Dr. Guy Steele who wrote Scheme, Dr. Alan Kay who invented Smalltalk, and Guido van Rossum who invented Python, among others. All of these people are trying to advance the state of the art of computer science, not take it backwards as Hacknot suggests.
I think DLs, with the exception of a few, represent a purist vision of the future of programming. Future programming languages will probably not look exactly like them, but their influence will most definitely be felt. It already has been. The ideas of OOP, introspection/reflection, closures/lambda expressions came out of work that was done decades ago in the DL realm. The truth is that modern programming languages are playing catch-up to what DLs represent. I personally, as a developer who cares about his craft, will be better off for it, thank you very much. Unlike Hacknot I don’t think dynamic languages are the path into a fever swamp for technology. Quite the contrary. They’re the next step in making our industry a mature profession, though they’re not the endpoint. Just a step along the way.
Giving credit where it’s due, a lot of what I’ve stated as problems with static languages, in terms of application development issues, have been solved in .Net 2.0, and recent versions of Java. I’ve read up on .Net 2.0, though I have not done any work in it yet. I look forward to seeing more of what Groovy can do, and what Orcas ends up being from Microsoft. I’d like to see these languages progress to include more of the powerful features I’ve discussed, down the road.
Read Full Post »