It may sound neat to allow developers to modify the language, but having used Smalltalk for more than 20 years, I have had to deal with the chaos that can result when different developers modifications conflict. I would rather have a controlled and organised process.So an ordered and controlled process is seen as desirable. Ok but controlled by who exactly? The truth is that most people feel more comfortable being lead. I can wax lyrical about the technical superiority of languages like Self, Smalltalk and Lisp as compared to lesser languages like Java and C# (and even Ruby and Python), but this doesn't matter a jot if people just aren't 'comfortable' with these supposedly 'superior' languages.
With Java there is minimal degrees of freedom. If you want to iterate, there is one (non-deprecated) way. Want a call back there is one way. You do things the 'Gosling way'. It is all pre-packaged and rather assuring. I must admit when I first used java I found it's simplicity re-assuring too. It was definitely welcomed after the explosion of constructs that accompanied the transition from C to C++. C# has used the same formula, after all it worked for Java. Java has taken "the shrink-wrapped approach" further, beyond the base language. The whole J2EE application stack was supposed to result in "one way" to build enterprise applications. Reducing software development to painting by numbers.
This all works up until the point where the 'one way', just isn't the best way for you. What do you do then? Well you live with it, like the EJB community did for years, or you jump to something better suited, like pico-container or Spring.
Many Java developers are now jumping to Ruby and Rails for precisely the same reason. For many web apps, the full J2EE stack even with Spring and Hibernate, is just seen as overkill. Interestingly though, very few have moved to Squeak and Seaside, and even fewer to Lisp. Why?
Well in Matz and David Heineimeier Ruby and Rails respectively, have strong leaders. Benign dictators that prescribe "how things should be done". Ruby developers can model themselves on the approaches recommended by these leaders. Better still these leaders are developers, themselves, so there is an instant bond of trust. The Python community has demonstrated this phenomena even more so, with a single all knowing leader Guido van Rossum. Rossum even dictates how code should be laid out and how tab spaces should be used!
So in contrast how do languages like Lisp and Smalltalk compare? Well let’s start with Lisp. I like to think of Lisp as a Meta-language; a programming language for writing other programming languages. A good example of this can be seen at the Vista Smalltalk blog. Peter Frisk is using Lisp to build a Smalltalk interpreter on top of Flex. So as far as Lisp is concerned, Smalltalk is just a DSL, created using Lisp macros.
With Lisp you deal with fundamentals. The smallest construct in Lisp is called an atom. An atom is a single character and you can combine atoms to produce symbols, and symbols to produce s-expressions (lists) etc, all the way up to a full class hierarchy of objects and associated functions. You can even determine how s-expressions are evaluated with Lisp macros, so basically you can do what you like!
This power puts a great deal of control and responsibility in the hands of the programmer. Of course there are established patterns to help guide you, but there is no benign dictator making a bunch of design choices upfront. You have to make your design decisions yourself. You are on your own!
Some people will revel in this power and flexibility. Others though, are likely to find it daunting! Smalltalk follows Lisps lead, but provides a lot more pre-defined structure. It has a small syntax, just like lisp, and like lisp has a meta-model built to support meta-classes, classes, and object instances. Unlike lisp though, all objects interact through message passing and are fully encapsulated. Many objects in Smalltalk are part of the language, such as the Context object used as a stack frame, Block closure object used as a lambda expression, and compiler objects used to turn strings into byte code. So Smalltalk gives you a lot of structure.
Smalltalk wears it's heart on it's sleeve. With Smalltalk all this structure is written in Smalltalk, so as a programmer you can change any part of it as you see fit. So this is fantastic if you want to create your own specific Smalltalk dialect. But if you do, Dan Ingalls or Adele Goldberg won't be there to help you out. And you won’t be able to turn to the Smalltalk-80 "Blue Book" either. You will be in the same camp as the Lispers, on your own!
When I first came across Smalltalk I saw all the dialects as a concern. All these semi-compatible versions surely can't be a good idea? As I have become more experienced as a programmer though, I have come to see diversity as a good thing. Two analogies come to mind. The first one is biological. In nature animals ensure that there is sufficient diversity in the gene pool. Each individual is not a clone of all the others, so if a sudden virus attacks, some of the species will be wiped out, but hopefully, others will have immunity, so the species as a whole survives. I think Smalltalk has this strength. Depending on what is important, there is a variant of Smalltalk to fit the bill, and if there isn't, a dialect can be readily mutated to meet the need (in most cases). Languages that can’t adapt in this way, face the risk of dying out through natural selection (something I believe Java is in danger of).
The other analogy is spoken language. Spoken language is a living and changing thing. We do not speak the same way today as we spoke 300 years ago. Also we have regional dialects, a Scoucer for example, sounds very different to a Cockney, yet they both claim to speak English (the Queens English, not US English :^)).
In their own domains Scoucers and Cockneys get on fine speaking their own dialect. But in situations where they may have to communicate with each other, like with written English, they both fall back to 'Standard English". For Smalltalk, "Smalltalk-80" is the equivalent of Standard English.
So that's the language landscape as I see it from a cultural perspective. Where I think I agree with Steve, is that change is slow in software because of a number of reasons, many of which are cultural. Where I believe things are inevitably heading though is into a pluralistic world containing many languages and dialects, but also sharing a common base, a lingua-franca. I see the lingua-franca as being based on late-binding and message passing, but I’ll save a detailed discussion of this for a later blog. In this new world I see many domains with leadership being dispersed across them, and with several individuals taking a leadership role at different times and in different circumstances.
For this to occur, developers will need to be more comfortable taking the lead themselves, and getting rid of the "training wheels". Technically, there are tools on the horizon that could help here, protecting the less self-assured. I see Language workbenches as described by Martin Fowler as perhaps helping here. A language workbench could provide a reassuring wall between the meta-language and the domain specific language, providing reassurance and safety for domain language programmers.
Supporting tools aside, with the rise of open source and open source languages, I believe there is strong evidence of this cultural change happening already! I see this change as inevitable as the industry grows up and matures.
11 comments:
A few comments:
....I think it is wrong to claim there is only one way to do things with Java. Sure, there have been the recommended ways, from Sun and JCP, but that has never stopped alternatives from becoming widely used and appreciated. Examples of this are Spring, Hibernate and AspectJ. I believe one of the reasons that Java is so successful is because it allows so many alternative approaches to thrive.
... as to who determines how the Java language changes; well, something like the JCP, I would suggest, but more democratic.
... I have my own criteria for technical superiority! I can wax lyrical about the performance, portability and multi-threaded nature of Java in comparison to Self and Smalltalk. I don't think there are any absolutes here.
... I think a lot of the flexibility of Smalltalk is wonderful, but can give rise to all sorts of issues. In past discussions I have shown many reports of different developers attempting to impose clashing modifications to the base system. The reason for Sun keeping java.* and javax.* for themselves and saying 'don't touch' is based on bitter experience.
.... I really have to disagree about there being any disadvantage in having a range of Smalltalk dialects. This does not protect the developer's investment, as I know from personal experience (having written lots of Digitalk code). Better to have one large 'standard' implementation, than many different smaller ones that come and go. And, Java + the JRE are here for the long term. There is not the slightest indication of any slowing of its adoption, or at least none I can find. Any successor language or languages are likely to sit on the JVM (or a future extension of the JVM) and interact with legacy Java libraries anyway, so investment is protected, in a way that simply was not possible with Smalltalk.
... I don't think 'training wheels' is a good analogy for the safety of systems like Java. I think 'seatbelts' is better. There is little evidence that developers now are any better at dealing with the freedom given them by dynamic and modifiable systems like Smalltalk that they were 20 years ago; the safety of Java (and similar systems) was deliberate - it was a reaction to the major problems that resulted from 'lack of seatbelt' coding, both in terms of the lack of memory safety of C++ and the modifiability of Smalltalk. Maybe I am being too pessimistic, and code package management systems can allow the power of languages like Smalltalk to be really safe. Even if they can, I would be interested in what advantages this kind of flexibility actually provides - what are the supposed advantages of begin able to modify the core system, of late-binding and message passing? I think that would be a very useful discussion.
Hi Steve,
First of all, I think you must agree that these cultures differences do exist. You only have to look at the reaction of the Python community at the suggestion of adding static types to Python as a clear indication of this. The response was a resounding NO!
"I would be interested in what advantages this kind of flexibility actually provides - what are the supposed advantages of begin able to modify the core system."
The purpose of this post was to explore the cultural issues from a non-technical perspective. It is true to say that my post has a "dynamic" bias which I think your comments have helped to balance. In my next post on Objects, I'm going to look at the technical advantages of late-bound languages and what I will call "blue" OOP.
For those who can't wait, take a look at the Self video. It is the first in the link to OO videos on the web, which I provided in my previous post to this one.
I am certain that cultural differences do exist, but that does not relate to the issues I mentioned. The people who choose to develop in Python are almost by definition a group that believe in the dynamic approach, so their reaction to static typing was predictable. It is a self-selecting group. The people to ask about this issue are those with years of experience of different approaches. Static typing has advantages, and that is why there are attempts to allow it as an optional feature in many dynamic languages; LISP, Python, Smalltalk etc. These attempts are done for good technical reasons.
Steve,
We are in violent agreement:
"I am certain that cultural differences do exist, but that does not relate to the issues I mentioned. The people who choose to develop in Python are almost by definition a group that believe in the dynamic approach, so their reaction to static typing was predictable. It is a self-selecting group."
The people that chose to program in Java is a self selecting group too. I think Sun relied on this to ensure Java's success.
We all tend to stick to what we know and where we are most comfortable. C/Unix was hugely succesful, and I'm sure that C programmers out numbered Lisp programmers by an order of magnitude in the mid-80's.
So it was easy for C++ and then later Java to tap into this ready made C/Unix culture/community. In contrast, Smalltalk had to build a culture and community of it's own from scratch!
I also agree that Smalltalk has issues and that many of these issues are home-grown. But Smalltalk has always had a fraction of the resources and attention of C++/Java with which to address it's problems.
In the big picture, I still see Smalltalk and Smalltalk inspired languages like Ruby and Python as having a promising long term future. These languages have survived the test of time despite always being in a minority!
Hi Steve,
I didn't address your last point:
"Static typing has advantages, and that is why there are attempts to allow it as an optional feature in many dynamic languages; LISP, Python, Smalltalk etc. These attempts are done for good technical reasons."
Static typing in itself is fine, the problem with C++/Java is early-binding. Early-bound languages have several draw backs. Frameworks like DCE, COM, Corba, OpenDoc, SOM, EJB's, Spring, and AOP just o mention a few :^) are all attempts to overcome early-binding. All of these have failed to a greater or lesser degree IMO. The best place to overcome early-binding is in the language itself.
The coupling of static typng with early binding is a major conceptual flaw in C++ IMO. C++ took the same approach as Simula, and in doing so misses the point of OOP. A Class is not a Type, it is that simple. Java does better with Interfaces, but still Interfaces are early-bound resulting in fixed Types at runtime and restricting polymorphism. So Java is less OO too.
I'm glad that you have pointed out that static typing can be combined with dynamic languages. The two issues are orthogonal. Infact Strongtalk does static typing better than Java does IMO. Scala's static type system is better than Java's, but it is still not as expressive as Strongtalks IMO.
Perhaps you should post an entry on your blog about static typing, and it's advantages. In my view static types are one of the most misunderstood and confused areas of 'pink' OOP.
Like I say, I will be addressing these issues in a future post.
Paul.
....In the 80s and early 90s, Smalltalk was not a resource-starved language. It was hard to pick up a computer magazine in the late 80s without a mention of it. It had large vendors like Digitalk and IBM behind it. The truth is that instead of working to make Smalltalk a truly high-performance language, vendors hand-waved away issues of speed. That was one of the reasons why so many Smalltalkers (like me) left.
...Spring has nothing whatsoever to do with overcoming early binding. It is a Dependency Injection system. That this is expressed in XML (which is interpreted at run time, so late) is only one way to do things. People can, and do, express Spring binding in static code, such as Java. Spring is no more true 'late binding' that using 'Class.forName()' is true 'late binding'.
.....And, there is a good reason for restricting polymorphism through the use of interfaces. It establishes a contract between different parts of code; a contract that need not wait until run-time to be checked. Duck typing can hide problems.
Hi Steve,
Some of what you say here is valid opinion. Other parts are factually incorrect.
Dealing with the facts:
"People can, and do, express Spring binding in static code, such as Java. Spring is no more true 'late binding' that using 'Class.forName()' is true 'late binding'."
This is still late-bound. I can write late-bound code using a static early-bound language. The difference is that it requires more code. For example the X11 GUI is a late-bound system, yet it is written in C. So Spring expressed in Java code using reflection and the dynamic proxy API is still a late-binding mechanism. The binding occurs at runtime. Think about it. If my Spring App is deployed as an expanded war, I could go and change the class files, and the Spring IoC late-binding would still work. Anything in Java that uses an Interface allows for late-binding. Binding to a class is fixed at runtime however, and is always early-bound. My point is that Java would be better served by late-binding mechanism that are 'built-in' to the language and are uniform across all object interactions, rather than relying on frameworks like Corba, OpenDoc, Spring IoC, AspectJ and Java Interfaces.
And the Opinions:
....I accept your view that Smalltalk had significant momentum in the early 90's. It is a shame that the first really fast implementation of Smalltalk, 'Strongtalk' came along too late and got consumed by the Java phenomena.
....The main reason I see for restricting polymorphism is to remove the overhead of message sends. Strongtalk has static types, but allows any object that satisfies the required interface (Type) to bind at runtime. Having dependencies on fixed Interfaces makes the entire system brittle. Interfaces change. This is the problem with RMI, Corba, WSDL etc. Imagine the web if you had to stop it, edit, re-compile and re-deploy remote stubs every time you wanted to add a new link to your web page!
....Statically typed code, whether it employs message sends or a virtual function call can have problems too. The best way to deal with problems (bugs) is to test mercilessly!
Hi Steve,
A correction, I said:
"Binding to a class is fixed at runtime however, and is always early-bound."
What I meant to say was:
"Binding to a class is fixed at compile-time however, and is always early-bound."
But I guess you knew this.
I wonder if there is some clearer definition of 'late bound'? What I was trying to say was that just because you can do late-ish binding in Spring, that does not mean that is what Spring is 'for', and that does not mean it was any motivation for the design of Spring. It is a side-effect.
I also think you are confusing things by associating AspectJ with late binding. Most of use of AspectJ is at compile time, by byte code enhancement.
However, a more standard Java method of dealing with these things would be welcome.
As for the testing; well, my opinion and experience is that you simply can't get the coverage with tests that you can with compile-time checking, and if you tried the volume of your tests would be phenomenal. There are so many possible routes through code that tests simply can't cover them. Last year, I posted an example in a forum on TSS, which described how a well-tested program written by one of the most experienced Ruby developers crashed simply because someone changed the order of 'require's in dependent code. This was because the dynamic nature of the code mean that new paths through the code were explored.
The nature of dynamic code is that it has almost endless potential for change, even at run time. The human mind does not have endless ability to devise tests!
Static code and type safety are, in my view, a powerful and welcome tool in the production of robust code (in combination with tests, of course).
This is an interesting discussion.
The definition of late-binding that I use is any binding of a method call to a concrete implementation that occurs after compile time. So any concrete method call that isn't hard-coded by the compiler at compile time.
Bruce Tate has written an interesting article that I think agrees with this definition:
http://www-128.ibm.com/developerworks/java/library/j-cb11076.html
As for testing, there is a place for static type checks. There is also a place for other types of static code analysis like Lint for C/C++ and Code Critic in VisualWorks. What we need to do though is separate Class from Type and Static type checks from early binding. These are all different things.
I recommend that you take a look at the Strongtalk static type system and you will see what I mean. I also recommend that you take a look at Scala.
As for testing, TDD says that you only write code in response to a test, so it is possible to have 100% code coverage. There are also code coverage tools to help here.
If your code never runs than how do you know it works? The only way to know that imperative code functions correctly is to test it.
If you can prove differently, I'd be very impressed. I'm still waiting for your post on static type checking!
Hi Steve,
Just to be sure that I've covered all your points :^)
"As for the testing; well, my opinion and experience is that you simply can't get the coverage with tests that you can with compile-time checking, and if you tried the volume of your tests would be phenomenal. There are so many possible routes through code that tests simply can't cover them"
This is why you isolate your unit under test (UUT) into small chunks. With TDD you do this using mock objects which are used to replace external dependencies and reduce the UUT to something testable. I agree 100% code coverage at an integration, or functional test level is very difficult, but it should be achievable at a unit test level.
Incidently late-binding helps with testing too. One of the main drivers behind Spring is that IoC allows component dependencies to be broken for testing purposes. With a late bound language there is no need for something like Spring.
Post a Comment