Psychedelic Panorama of Foo

Á¦ À̸§ÀÌ Inigo Montoya ÀÔ´Ï´Ù. ´ç½ÅÀÌ ³» ¾Æ¹öÁö¸¦ Á׿´¾î. Á×À» ÁغñÇØ.

ÀÏ¿äÀÏ, 8¿ù 03, 2008

 

Static vs. Dynamic Languages (Loose vs. Strict Typing)

¾Æ³çÇϼ¼¿ä. Recently, someone asked me about the difference between strict and loose typing. Specifically, "Why would anyone want strict-typing in the first place?" At first, I was a little stumped myself. I'm not good at making strong arguments impromptu. I always come off as sounding like I don't know what I'm talking about. Usually, it's something I have to take time to think about. I deliberated on this, and I even asked for some input from some friends. This is something I've argued pretty frequently in the past, so I know just who to ask for with counter-opinions.

I'm going to come to the defense of strictly-typed languages because I'm assuming we all know why we want to use loose-typing; therefore, I'm going to try and explain "Why strict-typing?" First, I'd like to explain their differences. Then, I'd like to outline the good and bad parts of each. Finally, I will give my own personal opinion in the matter. All of it will be from the perspective off "Why strict-typing?"

What is the Strict-Typing?

Static languages are languages that are strictly-typed. This means that when a variable, method, or anything really is defined that a type must be explicitly declared as well. Take the example in java:

public class SomeClass { @Override public String toString() { String retval = new String(); return retval; } }

You may observe that there is a type required for the class definition. Within the class is a method toString() whose signature has a String type declaration; Likewise, within the method is a variable explicitly declared as type String that is then explicitly returned. It's true that you could do:

return "";

Most any language can do that though. My point is aimed specifically at variable declaration though. Rather, how would I have to declare the variable? That's when the strict-typing comes into play. Probably the most extreme example of strictly-typed language is "C":

typedef char(*(x())[])() RandomWord; RandomWord randW = newRandomlyGeneratingWord();

The above describes a type where each character in a null-terminated character array actually points to a function that returns a random character. That means that each time the character array (String) is read, it's completely different. The important part to notice is that the type has to be explicitly declared. The typedef is there to show that one type can be defined as another. Each thing I just described is an example of strict-typing within a language.

What is Loose Typing?

Not all languages are like this. Loosely-typed (dynamic) languages are not. Take for example Perl:

{package SomeClass; sub new { foreach my $arg (@_) { # do some stuff } } sub toString { my $str = ""; return $str; } }

Where are the types? "Don't know. Don't care." Pretty simple, right? Then, why bother with a static language at all? Dynamic languages seem simple enough, and we don't have to deal with the hassle of types. Well, the thing about dynamic languages is that most of them are not compiled. That puts into a sub-discussion of compiled vs. interpreted-uncompiled.

Compiled means that before it can run the code goes through syntax checking, validation, and optimization. Either bytecode or machine code is produced as a result of compiled software. This code typically can run much faster than interpreted code because interpreted code typically runs within a specific runtime environment which gets its own resource space. This can be a performance hit. Another performance it is done by actual parsing of the language. Compiled languages can defer this to the compilation step, so parsing doesn't happen during runtime. To counter, compiling code is an extra step. interpreted code can just run, but compiled code must be preprocessed and processed before actually running. In short, you are given a choice. Do you want to take extra time to build the application or have the application take longer to function?

That's just compiled v. uncompiled. What if you want to compile a dynamic language? Does that mean it will run faster than an uncompiled language? Yes. Take groovy for example. When it is optimized by being compiled into java bytecode before execution, it runs faster than flushing the groovy code directly through the interpreter. The same goes for python. When pyc (python compiled bytecode) is used instead of py (normal python code,) it tends to run faster in the runtime environment because it's been optimized. What does this mean? Well, it means that for some languages compilation cannot be a factor when comparing static and dynamic languages. For example, if you want to compare java and groovy, you have to leave out the compilation part because both will compile to java bytecode.

You'll have to make the decision for yourself when you weigh in dynamic or static language whether you want to consider compilation as a factor or not. I do consider it because I typically do not care to compile my dynamic language even though it is possible. To me, that takes away one of the reasons to use a dynamic language which is simpler deployment. When you start to make your dynamic language more static, you lose more of the reasons why you want to use it in the first place.

Which brings us to...

What do we Get From Static Languages

Above, I just illustrated my first point. "Why strict-typing?" Because it is compiled. Compiled means its faster and more efficient. I know what you're thinking. "That might have been important to me 20 years ago." True. It's not a very strong point, but it is one. I'll explain some more below.

Forces the developer to think in terms of code reuse

Static languages are not very forgiving when it comes to API's. Take Java for example. It has access control for methods which most dynamic languages don't have. This can really effect what external classes get to use internal function. For OO languages, we have to consider polymorphism. Java, for example, requires interfaces to fulfill polymorphism. Interfaces as we know do not have any implementation whatsoever. All methods within them are declared public. When we want to use polymorphism, we're forced to think about code reuse, API's, etc... because extends only gives us single-inheritence. With generics and autoboxing, we can let the compiler handle optimizing these types and handling type correction for us. We don't get this in dynamic languages. Type correction is something we have to worry about on our own. This makes static languages muuuuch more useful for creating API's and frameworks.

Static Languages are easier to read

Perl has earned a reputation as being the "write once, read never" language. Mostly because types are sooooo hard to determine. You can have an array within a hash with in an array of references to functions that return objects blah blah blah. It's crazy. Unlike C, there is no typedef that declares something so complex. You have to figure it out.

Ruby and Groovy are really concise languages, but a lot of the code is difficult to understand because of closures, mixins, in-line anonymous classes, etc... Sometimes, code can almost look hackish because we know what it does (according to documentation) we just can't understand how it's doing it by looking at it. It's magic.

Static languages give us method signatures which let us know what parameters a method takes, what it returns, and what exceptions (if any) are thrown. Sometimes we can get even more information. With IDE integration, this is great because you can have the IDE deal with things like creating methods for you based on parameters, return values, and exceptions. A friend of mine expressed frustration about this in dynamic languages because he feels he's forced to read the code which puts him in a tough spot. He has to write code with explicit knowledge of the internals. This speaks to the previous point about API's and frameworks. Without API's, frameworks, method signatures, etc... there isn't any abstraction. You have to develop knowing internals of the library or have an extremely strong faith in the documentation. Excellent point Andrew!

Static languages are different. Types are easy to understand because they are explicit and simple. Java for example, we can easily determine all of the aggregates of a class just by looking at it. With an IDE, we can determine exceptions, signatures, errors, code completion, and code cross-referencing very easily. This brings us to the next point.

Static Languages play nicer with IDE's and tools

Because of strict typing, these languages are easier to parse than dynamic languages. Therefore, it's easier to do cross-referencing, debugging, type hierarchies, documentation, and code completion within IDE's. That isn't to say that dynamic languages cannot get this from an IDE. It's just more difficult. Java, for example, has just about every IDE under the sun supporting it; however, it is very difficult to find a good IDE for Ruby, PHP, or Python. Forget about finding one for Perl. For Ruby, I hear that IDEA is great, but Eclipse is pretty good for PHP, and Komodo for Python. Getting your favorite IDE to support all of them is just impossible. Even when an IDE does support it, it doesn't work as well as it does with a Static Language like C or Java.

Further, tools are difficult to obtain for these languages that work in your environment. Just try and find a line-by-line stepping debugger that integrates with your IDE, mod_python, fastcgi, etc... It's just not happening. Scala and Java let us use the Java Debugging Framework for profiling and debugging code. We can use it with IDEA or Eclipse which also happen to support tomcat and Jetty appservers. It's Nirvana!

Static Languages are compiled

I can say this because they have to be. There is no way that an uncompiled language can handle the type correction unless it was preconstructed to do so. It's just logically impossible.

Dynamic languages run into more problems here than just performance. You actually can't validate an entire application until you run it. Which means there can be syntax errors in the code, and you would never know. With compiled code, you know for a fact that there aren't any mismatched type conditions, syntax errors, etc...

I'm going to quote Andrew again here. He made a good point about the community regarding this opinion. Andrew, I totally agree with you.

"The Ruby/Python zealots will tell you that Unit Tests will solve nearly all the problems above, if done right. That's nice in principle, but I shouldn't have to do grunt work, and ruby/python at least force you to do so much that the computer or IDE could be doing for you. It's like digging ditches with a hand shovel, because it 'enforces good posture' or some garbage." --Andrew Hollamon

What do I Think About Static v. Dynamic Languages?

I think they're both great. There's reasons to use both. I think it's important to know what those reasons are because you don't want to use the wrong one just because you don't see the point in using the other.

How do you know when to use which one?

I tend to go with static languages for projects that require long-term sustainability. It's just better for management overall. Resource management is less of a hassle because we know the code is easier to train people on because of common API's and frameworks, as well as strong documentation and understandable code.

Smaller projects like POC or CRUD applications are probably better in dynamic languages. Rails for example is perfect for CRUD because a lot of the code is written for you. The conventions it uses make it less necessary to understand the code. What then when you have to make your application more complex? Well, then I would start looking into a static language. The last thing you want in a complex project is "write once, read never" code. This is especially the case if it is for a professional project. If it's personal, do whatever you want. It's your own grave.

·¹À̺í: , ,





This page is powered by Blogger. Isn't yours?

¿¡ °¡ÀÔ °Ô½Ã¹° [Atom]