Monday December 19, 2005
I sure stirred up a pile of flaming with my previous post. The internet
flame-before-reading tradition is alive and strong. I've always been a big
scripting language fan (the first language I ever learned was a
"scripting" language called FOCAL).
Looking at the design tradeoffs between the two language categories has
always been fascinating.
One of the slogans that was brought up in the commentary to my previous blog entry was "freedom vs. safety". Once upon a time I used to believe that: it has certainly been conventional wisdom for years. But a lot of the time, the truth is actually that safety is freedom (eg. a good safety harness and rope give you the freedom to climb a mountain).
When I'm writing a function and declare a parameter to be an Image, I am free to trust that it is an Image. I'm free to trust that no one's array access has smashed my data structure. Examples abound.
Posted by Tom Ball on December 19, 2005 at 09:40 PM PST #
Posted by leouser on December 20, 2005 at 06:24 AM PST #
I have two frustrations with Java's type system. First: the type system and the security system is so completely tied together that you have to resort to black magic to treat objects with fine-grained controls (something useful for component containers). Second: the method invoked is decided at compile time instead of selecting the most appropriate method at run time. That means a recompile is necessary to call a more strongly typed variant of a method when a class is updated.
I don't have that problem with some of the more recent scripting languages--and they are just as strongly typed as Java. They have the safety you speak about. They just don't have the frustrations that I have to work around in Java. Of course, they do let you invoke any method you like as long as it exists.
Getting back to the security system, I can't understand how you can load the exact same bytecode in two different classloaders, and not have the classes be equivalent. All that would be desired is to have the permissions different depending on which classloader's version we called. It makes no sense--which is why Java's security system needs an overhaul. It only comes into play here because it is tied to the type system.
There is a point where there is too much "safety". "Any society that would give up a little liberty to gain a little security will deserve neither and lose both." -- Benjamine Franklin.
Posted by Berin Loritsch on December 20, 2005 at 07:09 AM PST #
Posted by James Robertson on December 20, 2005 at 08:03 AM PST #
Posted by leouser on December 20, 2005 at 08:46 AM PST #
class Called { public void print(Object o) { System.out.printf("Object: %s\n", o); } }If it was called with this code: You get the phrase "Object: 45" printed. If the library is updated and called has a new overloaded method that accepts the Integer object nothing changes:class Called { public void print(Object o) { System.out.printf("Object: %s\n", o); } public void print(Integer i) { System.out.printf("Integer: %d\n", i); } }The code will still print out "Object: 45" until you recompile against the new library. Then you will see "Integer: 45". Statically typed.Posted by Berin Loritsch on December 20, 2005 at 09:13 AM PST #
The security model relies on permissions, which you query the static AccessController to determine if you can perform some action. These permissions are again set statically within a ProtectionDomain which is locked in at load time. The reason your isolation works is because the two classloaders have different ProtectionDomains. The only way to provide a different set of permissions to classes within the same JAR is to override the classloader to assign the different classes a unique ProtectionDomain.
Quite often it is useful to have interfaces within the same ProtectionDomain shared across implementations so that it is easier to use the components that belong to separate ProtectionDomains to further restrict what they can do. However, most interfaces are included in the JAR along with the implementations. The only other trick is to generate dynamic proxies to invoke methods on the object that implements the interface in the opposing ProtectionDomain.
Lastly, I don't like things being static as a general rule. Static fields force the need for overloading the classloader to perform a function it was not intended for. Classloader tricks are error prone and ugly.
Posted by Berin Loritsch on December 20, 2005 at 09:23 AM PST #
Hi James.
It's nice to see you're starting to address this topic more head-on in these last two blog entries.
My sense from wandering around the web is that advocates of the new "dynamic" languages feel these are fundamentally different from, and a great improvement over, both untyped (or loosely types) scripting languages that came before, and also an improvement over Java, because Java's static type checking causes so much busywork (in their argument) and in the end the modern "latent" type-checked languages do check types, but at runtime. The argument is also that static type checking doesn't buy you as much when you can launch your code immediately (without a slow compile step) and test it, and that developers aren't seeing their code blow up at 30,000 feet, so to speak.
Right now it seems that Sun is responding by pointing out how solid and great Java is, but an alternate approach is to suggest that the JVM is a much better home for dynamic languages than either the CLR or home-grown VMs. While it's great to have a statistic that there are 200 "languages" for the JVM, from my own experience these have negligible use outside of the Java community (and in many cases, within the Java community), and almost no use overall compared to Python, Ruby, Perl, PHP, etc. The problem seems to be the messiness in using JNI (many of the new dynamic languages rely on compiled libraries for extensions), difficulty in some types of dynamic invocation, among other things. So what can Sun do to make the JVM more palatable for dynamic languages? And how many years do we have to wait?
Anyway--this long comment just to say that as a Java user and promoter, I think Sun should face this "rising tide" of dynamic languages with a welcoming attitude, as both sides stand to benefit from each other. Welcoming meaning--let's hear both sides state more openly where the benefits and disadvantages lie, admit them, and see where everyone can benefit from the larger set of tools available.
I am glad about the scripting support being added to Mustang, by the way.
Cheers! Patrick
Posted by Patrick Wright on December 20, 2005 at 09:26 AM PST #
Posted by leouser on December 20, 2005 at 10:44 AM PST #
Posted by leouser on December 20, 2005 at 10:45 AM PST #
We can agree to disagree about the statically checked types. C# behaves the way I would expect in this case, and Java behaves the way you would expect (note, C# does a number of things more that I would not expect--part of the reason I never attempted to learn it in depth).
I crave simplicity in my code, and there are a greater number of ways to achieve that if I don't have to worry about types--i.e. if you tell an object to quack and it obeys we are all happy. The bottom line is that your style of programming changes when you think dynamically as opposed to statically.
Don't get me wrong, there are a number of things I like about Java. I make my living off of Java programming. I've done many advanced things, looked at the results, and had a strange mixture of the joy of conquering and remorse of the hoops I had to jump through to get the job done.
Along comes languages that have lived about as long, but not really corporately backed, and it fits most of my needs very well. Since I employ TDD near religiously, I don't miss the type declaration abilities of Java in the least. In fact I am amazed at how much more efficient I am in getting a solution done.
I'll be honest that much of the efficiency comes from other improvements other than the type model. For example, the ability to edit in place, the ability to pass in closures to a method, etc. Even the ability to add new methods to a class is terribly useful in some cases (e.g. mixins).
So again I am faced with a strange combination of feelings. I am growing more disalusioned with Java because I can be more efficient with other languages, but at the same time I want Java to be better. Naturally we can't all agree on the "one true way" which is why there is diversity in programming languages.
I just don't like broad sweeping generalities that lump old weakly typed languages with new strong dynamically typed languages. I'll concur that not everyone will agree with my views on language design, and there are reasons for whatever road you pick. I just find myself stumbling over the same features that are designed to "protect" me. I'm not your average newbie, I know some fairly advanced stuff with Java. My least favorite being, "we won't write code that implements your interfaces because it polutes our project--even though we are using your code".
That inane philosophy of Not Invented Here (NIH) syndrome is a cause asking for duck typing. In some ways I have worked around this political problem using reflection and idioms, but it is a terribly clumsy solution to a problem that shouldn't exist. Then you have the whole problem of interfaces that are similar or identical in purpose and declaration but because they are different interfaces (different classes altogether) you can't use them interchangeably. I've used dynamic proxies in those cases as well, but again it is an inelegant solution that wouldn't be necessary without static typing.
Posted by Berin Loritsch on December 20, 2005 at 11:15 AM PST #
Posted by leouser on December 20, 2005 at 11:52 AM PST #
Posted by functionform on December 20, 2005 at 01:40 PM PST #
Posted by am on December 20, 2005 at 01:43 PM PST #
Posted by functionform on December 20, 2005 at 02:52 PM PST #
Posted by Slava Pestov on December 20, 2005 at 05:33 PM PST #
Posted by leouser on December 20, 2005 at 06:45 PM PST #
Slava: it's true that people write c**p in every language available to them. I would restate functionform's complaint in a different way: are there limitations imposed by dynamic languages that make inheriting codebases harder? For example, if parameter types to a function/method are not declared, then reading the code you have to infer what the type of that parameter is (maybe by reading all around the code). Or maybe you can't rely on the compiler and syntax tree to tell you what methods are actually bound on a given call--you have to use text searches and hope you found the right one. Those limitations, whatever makes it more difficult to inherit a large codebase, are something we should be able to talk about more clearly. With Java, it's certainly a hassle how much configuration is now maintained outside of the application classes themselves.
I think there is a lot of confusion in this debate (not just here, but all over blogolovia) in large part because people are speaking out their frustration in working with one language or another. And it seems to me (as I wrote earlier) that Sun should confront that frustration more openly as regards Java, because *that* denial is causing more frustration. People want to be heard. I've just joined an ongoing J2EE project, and the number of layers of code + configuration makes my head ache. Isn't this patently clear to anyone who uses it?
I doubt that Java is past its prime, but the fact that well-known writers and bloggers are moving away from it (at least to try something new out) should tell us that there are problems not being addressed.
Patrick
Posted by Patrick Wright on December 21, 2005 at 02:13 AM PST #
James,
You are conflating positive freedom (freedom to do something) with negative freedom (freedom from some constraint). Yes, Java/C# give the developer assurances that they won't overstep , which is what makes them so suited to corporate IT development.
The difference between designs, whether of computer languages or whole societes, guided by positive versus negative freedom is that designs based on positive freedom are aimed at fulfilling some ideal or telos of the designer and thus not very accomodating towards conflicting ideals. This can be seen in socialism, and in an extreme form communism. I'm not implying that Java users are communist ; ) Its just that Java is pretty single-minded on alot of things, very verbose, and not very flexible. Myself, as a programmer, much prefer the flexibility, and the attendant pitfalls, of C++/Lisp.
Posted by Brian Azzopardi on December 21, 2005 at 02:23 AM PST #
Because there is no type specified in methods, there can be no overloaded methods (see the code example above). Because there is no type specified, I just need to pass in something that supports the operations performed on the object I pass in. Most of the time that's fairly easy, and avoids the whole "value object" necessity. Many times you also have fewer parameters as well.
Posted by Berin Loritsch on December 21, 2005 at 06:48 AM PST #
Posted by leouser on December 21, 2005 at 10:46 AM PST #
For this to work, you should program to <em>operations</em>, not <em>methods</em>.
Posted by Daniel Serodio on December 21, 2005 at 01:11 PM PST #
Posted by leouser on December 21, 2005 at 03:08 PM PST #
Imagine that I provide you with a library containing <code>executeCode( Object o )</code> ... and you write code against that. Now I release a new version of my library, and without even overloading <code>executeCode( Object o )</code>, I change its functionality. Now, even in the Java compile-time-bound world, the functionality of your program will change.
You state that "because a method shares a name with another method does not indicate with any certainty that they do the same thing." In reality, even if a method shares its name AND parameter types with an older version, it might not do the same thing.
Replace an existing method with updated functionality could be the whole purpose of releasing a new version of my hypothetical library, and it's one of the main ideas behind OOP, as Daniel explains above. I think you missed his point about programming to operations instead of methods.
Obviously you trust me as the library implementer to not change things TOO drastically on my existing method. If I don't give you the source, or if you don't take the time to really study every change, you won't be sure, but that's part of my implied obligation because if DO break things for you, you'll stop buying my library.
So now if I introduce <code>executeCode( Subclass p )</code>, you would generally trust me not to break things.
In fact with Java you could see some weird issues... if you drop in version 2 of my library, with the new overridden method, on your live server, it won't use the new method (no recompile). But then if you load things up in your IDE, you'll probably be using my new override method. Could get messy. :)
Anyway... if you accept the potential value and purpose of altering the functionality of the existing method (again, Java already supports this inherently), it shouldn't be much of a leap to see the value of run-time polymorphism instead of compile-time polymorphism.
Posted by Ben on December 21, 2005 at 11:01 PM PST #
Posted by Henry Story on December 22, 2005 at 12:02 AM PST #
Posted by leouser on December 22, 2005 at 06:29 AM PST #
Posted by Tom Lord on December 22, 2005 at 09:09 AM PST #
Any way, I think it is not possible to change the language to acomodate this feature. Code obfuscators already use the property of compile-time polimorfism:
void add(int i){...}
void subtract(byte b){...}
can be potentialy converted by the code obfuscator to:
void a(int i){...}
void a(byte b){...}
and the virtual machine would execute the code without complaining, because it differentiates the two methods by their parameter list. But if runtime polimorfism was permited right now in Java, calling method **add** passing a byte could have the virtual machine actually make the call of **subtract**, if you used code obfuscation.
Posted by Fernando Kronbauer on December 22, 2005 at 09:52 AM PST #
Leouser, Java has carried over some of the bad habits of C++ while trying to learn some (albeit very few) lessons from SmallTalk. I would suggest taking the time to learn SmallTalk enough to get used to it. The process will cure you of the uneasy feelings you may have.
Bottom line is that any time you introduce logic based on type--whether overloaded methods or <tt>instance_of</tt> checks--you are not using Object Oriented Programming/Design. You are using Object Based Programming which is different. Java carried forward the C mentality of structs and functions and put them together in an object.
A library that is designed for polymorphism and to be extended without rewriting sections of code is a very beautiful thing. I have yet to need to check for a type of an object in SmallTalk or Ruby. IMNSHO there is something wrong in the design if you believe you need type checking.
Posted by Berin Loritsch on December 22, 2005 at 10:44 AM PST #
Posted by leouser on December 22, 2005 at 12:18 PM PST #
Posted by Berin Loritsch on December 22, 2005 at 01:42 PM PST #
Posted by leouser on December 22, 2005 at 04:01 PM PST #
What is cool is that you can modify your class on the fly and play with it interactively to get a feel for how the object is used.
The differences are subtle, but I find it helps to go back to the source to really understand a concept. Too many people develop <em>using</em> objects instead of developing objects. It's a subtle distinction that will change how you look at problems.
Posted by Berin Loritsch on December 22, 2005 at 07:36 PM PST #
Posted by leouser on December 23, 2005 at 06:38 AM PST #
I think such checkings can't be done at class load time, the same way that virtual functions can be devirtualized only at runtime, if de virtual machine determines that there is only one implementation of it.
Let's leave Java as it is, for writing high performance components, and than have the scripting language of your choice to bind them.
Merry Christmas
Posted by Fernando Kronbauer on December 24, 2005 at 04:00 AM PST #
Before you say anything, I did not mean Smalltalk is a scripting language. I just meant scripting languages are suitable for binding and integrating components writen in other languages.
Cheers
Posted by Fernando Kronbauer on December 24, 2005 at 04:25 AM PST #
Posted by Berin Loritsch on December 24, 2005 at 12:02 PM PST #