Static Typing: Give me a break
scientopia.org
scientopia.org
I used to mostly use dynamically types languages: Python, JavaScript, Perl, Scheme. I also used Java and C++, but did not think they were worth the sacrifice in productivity. However, recently, I've been using Haskell. I really like it for reasons of correctness; however, I was also pleasantly surprised to find I was more productive in Haskell than in any dynamically typed language--even Racket (a Scheme variant).
There is one very simple feature in Haskell that makes it concretely more expressive than a dynamically typed language: typeclasses. You can have values polymorphic on their return type, which makes things like overloaded string and numeric literals possible.
However, it's more than this. Types help me think about my problem and constrain the solution space. I often overcome logical problems by figuring out how the yours should match.
There are a bunch of factors like that, but they really are a matter of preference. Typeclasses aren't: you simply can't do certain things as easily without them. QuickCheck is another great example; it's far easier to use in Haskell than in, say, Erlang.
For my most recent project, I was able to use 18-bit words very easily. Moreover, it would be trivial to make my code generic over the exact type of number used! I'm thinking of passing in probability distributions instead of numbers per se. That's also easy thanks to typeclasses.
In summary: I've found static typing can be more expressive, partly out of preference but largely thanks to typeclasses. Somebody once asked me what I thought the most important feature of Haskell was. I said typeclasses, and I still stand by that.
Programming languages sit at the mind/machine interface, I don't think it is very useful to evaluate them solely from a PLT standpoint.
It is possible for a language to not have these declaration and still be static (ie; compile-time type inference).
- Static Typing: "A programming language is said to use static typing when type checking is performed during compile-time as opposed to run-time." [1]
- Dynamic Typing: "A programming language is said to be dynamically typed when the majority of its type checking is performed at run-time as opposed to at compile-time." [2]
- Strong Typing: "A type system is said to feature strong typing when it specifies one or more restrictions on how operations involving values of different data types can be intermixed. The opposite of strong typing is weak typing." [3]
- Weak Typing: "One claimed advantage of weak typing over strong typing is that it requires less effort on the part of the programmer because the compiler or interpreter implicitly performs certain kinds of conversions. However, one claimed disadvantage is that weakly typed programming systems catch fewer errors at compile time and some of these might still remain after testing has been completed." [4]
Links:
[1] https://en.wikipedia.org/wiki/Type_system#Static_typing
[2] https://en.wikipedia.org/wiki/Type_system#Dynamic_typing
"There is a wikiality driven split in the English language.
Camp A says that a strongly typed language prohibits immoral implicit conversions, such as "1" + 1 => 2 or even more grotesquely, "1000" == "1e3" => true.
Camp B says that a strongly typed language gives each value a type, and that all operations which do not have well-defined semantics will signal an error rather than allow operations to execute which assume incorrect typing.
Camp A currently owns the articles "weak typing" and most of the article "strong typing". Camp B is settling for teaching the controversy in "Strong vs Weak typing" and putting passive-aggressive little notes on all the articles that Camp A's view is mistaken. "
Did I get it right? If so, camp B is correct and camp A are being dicks about terminology that they're getting wrong.
I have no opinion on the bit after that. :-)
I think some of the confusion is that the nature of the two sets of labels are commonly misunderstood. They're supposed to represent two orthogonal dichotomies about how languages design. In truth there is an orthogonal pair of dichotomies in there, but it's not captured by that set of labels. And the labels aren't really orthogonal.
The real dichotomies concern two different places where you can track type information: It can be associated with variables, or it can be associated with values. Some languages that represent the possibilities well are:
- C: Variables, but not values
- Ruby: Values, but not variables
- C#: Both values and variables
- FORTH: Neither variables nor values.
As for how they're commonly described: C is the classic example of static and weak, and C# is a good example of static and strong (though admittedly it has facilites for switching both off in a controlled manner). Ruby is dynamic and strong.But nobody wants to call FORTH dynamic and weak, and for good reason: The very phrase "dynamically typed" implies that types are tracked, just dynamically. But FORTH doesn't track types at all; it's much more correct to describe it as untyped.
So that's where it breaks down: While strong vs. weak corresponds well to the question of whether types are associated with values, dynamic vs. static is not actually an orthogonal dichotomy, because the word dynamic describes a decision about both ways of tracking type: Dynamic languages associate type with values but not with variables. And my earlier description of Ruby as "dynamic and strong" is redundant.
There is strong typing vs weak typing as well, and either can appear in a dynamic language. There also allowing nulls and also having or not having algebraic types.
> That's part of a more general preference, for preserving information. When I'm programming, I know what kind of thing I expect in a parameter, and I know what I expect to return.
Doesn't a functional single assignment language like Clojure or Erlang then make most sense? If your goal is to preserve information and knowing that no funny business has been happening behind your back (when you are not "figuratively" looking) isn't that what you want? This is even more important if this is a concurrent program.
You also have to ask yourself, what is the overall goal here. Ok maybe it is just aesthetics, maybe there is a small or (not so small) amount pleasure derived in figuring out the types and specifying that. Maybe it helps larger teams work better. Or it helps the compiler to make faster code (v8 might disagree though).
Most people would perhaps arrive at -- "I want my program to not crash as often". One way to do it is to then have a provable correct program. For avionics there is some degree of that. But it is very expensive. But another way is to assume your program will crash, no matter how strong your types are. Then fault isolation is a better strategy.
Most of the hangups with static typing seem to come from three problems: Excessive boilerplate, excessive "type purity", and excessive compilation time. I would also argue that a lot of the hangups with static typing come from Java.
Java requires a lot of boilerplate to manage class constructors, etc. However, this is greatly reduced in new languages thanks to type inference.
Java is supposed to be about "pure" OOP, making it very difficult (and slow) to invoke any sort of dynamic method. It can't even type arbitrary methods, once again due to its object-centric focus. However, newer languages are offering more ways of dealing with different type systems, and/or dodging typing altogether in certain cases.
The last problem is with compilation time, which can eat into development time the same way that testing does.
There's newer static-typed languages that are making big improvements across all of these problems. Scala, Haxe, Dart, and Typescript are ways of making development easier for pre-existing platforms... either by relaxing type issues on the jvm, or providing type features for dynamic runtimes (js).
On the other hand, dynamic languages are starting to get very sophisticated linters that are catching classes of errors that are typically found by static typing.
So, in a way, both camps are addressing problems in their own way. I'm primarily in the static camp, but I develop using a lot of dynamic languages, and have been impressed with the tooling and resources available there. However, I see more improvements coming from the new static languages.
Good point. More modern languages fix some of the flaws found in Java. Among which:
Scala (not really "modern" since it's ten years old, but more recent than Java):
* No boilerplate: check
* Type inference: check
* Compilation times: er... horrendous. C++-template-linking horrendous
My sweet spot these days is Kotlin, which checks on all three points (I use both Scala and Kotlin in IDEA and it's not even close in compilation times).
The Haxe->Neko compiler is fast past any expectation, when I started using it I simply could not believe how fast it went.
It's becoming increasingly hard to justify using a dynamically typed language these days.
Do you really think the use of dynamically typed languages is an important enough factor to require justification? I don't want to imply that the distinction isn't significant. I'm sure it is. I just haven't had any experiences where it came into play when making a language choice, and I'm curious whether you can elaborate on the need for justification.
Yes, because when you pick a dynamically typed language, you are giving up on important advantages that become crucial as your code base grows, among which:
- Type annotations, which make it easier for newcomers to understand the existing code base (and obviously, catching early errors by the compiler).
- Automatic refactorings, which guarantee that your technical debt remains at a reasonable level since it's so easy to evolve your code base as it grows. Even the simplest refactorings require human supervision in dynamically typed languages (here is a good explanation why: http://goo.gl/SKaos )
Static type checking attempts to use compile time information to make claims about the runtime behavior of a program. This technique obviously fails to the extent that the runtime environment diverges from the compile time environment. Dynamic type checking, on the other hand, has no such limitation: it has perfect fidelity to the runtime environment because it occurs within that runtime environment. Even if the runtime environment itself changes at runtime!
Here's a concrete example. You compile a Haskell program that dynamically links a package. You then install a newer version of the package, which has an incompatible API change. Your program, which reported no type errors, now has a type error (a static typist may not agree this is a type error, but a dynamic typist would assert it is). And if your program starts at all, it will likely fall over and die, because GHC's codegen is brittle against such changes.
Fortunately, we do not suffer this fate on platforms like OS X or iOS, where binaries routinely run on multiple prior and future releases of the OS, with their accompanying shared libraries, while taking advantage of newer features whenever available. ObjC's dynamic typing is a big help here: it's simple to express "call this method, if it exists." And if you call a method that does not exist, you get a readable exception instead of being sent wildly through a random slot in a vtable.
(And oh yeah, having a stable ABI helps too.)
So this is one of the big strengths of dynamic typing: it works even if you don't know where exactly your program will run.
Either the code you are writing is 100% in the dynamically typed language and it couldn't care less about dynamic linking, or it's invoking functions in a dynamically linked library through an FFI, and it will fail like any other language.
The binary brittleness of C++ and Haskell is not a direct consequence of their lack of dynamic typing, but it is a consequence of the underlying assumption: that the compile time environment fully describes the runtime environment, and so no provisions need be made for their divergence.
I don't understand your second paragraph. Maybe you think that code written in dynamically typed languages cannot be compiled into shared objects? I work on a large Objective-C dynamic library, which is compiled to machine code and loaded by the dynamic linker, just like a C library. The dynamic aspects of ObjC pay us big dividends.
Without them, you're on your own. And, for almost all values of you, that means you are pretty much guaranteed to have serious holes in your software. It's not like you can test these holes away. If you don't realize that you need to escape string X into the language of string Y before combining X with Y, do you think you're going to know to write the test that checks for whether you failed to escape X?
EDIT: minor tweak for clarity.
A simple example: suppose that every web form input is mapped into an "UnsafeUserInput rawtype" (that is, an algebraic data type parametrized over the raw input type). By simply ensuring that your output code only works on "SanitizedUserInput" types, you enforce that at some point in the code you apply a sanitization function to any unsafe input. Now the compiler is working for you, checking that your overall data flow respects the constraints you've encoded into the type system. In this style of programming, the type system becomes a way of encoding your desired integrity constraints inline with the functions and data types rather than outside the application logic in test suites.
http://blog.moertel.com/articles/2006/10/18/a-type-based-sol...
(That was written over six years ago. Today, there are even better solutions that don't just apply static types to strings but to the language fragments within them.)
string a = get_input_from_user();
string b = sql_lookup(database, a);
insert_into_output_html(b);
requiring instead UserInputString a = get_input_from_user();
SQLResultString b = sql_lookup(database, ConvertUserInputToSQLQueryInput(a))
insert_into_output_html(ConvertSQLResultToHtml(b));It's also one I implemented with relative ease in Python. The error is deferred, but other than that the implementation works equally well. Once again the question is whether up-front errors are worth static typing, and once again the answer is a preference.
And another possible interpretation is that the comic just expresses the author's preference (pun intended) of type category as a topic of discussion and thus finds the "dynamic vs static language" discussions uninteresting (because, let's face it, they are almost never about type category).
My interpretations might be completely biased though, as the first comic from CCC that i read was this one (http://ro-che.info/ccc/18.html), which could have caused a personal bias because of the good first impression :)
He's very opinionated and doesn't seem to know the subject material very well. He's just expressing his opinion slightly more loudly and eruditely than others.
I don't see the redeeming value here.
I don't miss the "I know he right answer" egotism I had in my 20s!
I dislike the "shades of gray/full colors" vs "black & white" thing, because IMHO, create the idea that exist several competing and contradictory correct answers to things. I tough still exist one puer solution, but several * approximations* that, because our limitations and/or limitations in the tools we use look like different (in computers, the final solution is expressed in assembler/bits. Languages are a illusion!).
But at the end, and with the age, I think is become clear that some questions beg for a better answers, but suck to solve it with our current approach. And I for example, all the time, working with python, obj-c, sql, delphi/pascal, javascript, coffescript, foxpro, html have the constant grief of "What if, I could to do this, instead of what I must do, because I can't with this tool!"
However, I firmly think is super-important to use several, opposite languages, to truly discover better ways to do things, learn more effective, faster, expressive or whatever approach to a solution, and broad the selection of tools to solve a problem. For example, when I use c# or obj-c, I tend to look the solution in python (my personal experience have taught me that python folks have the super-easy-to-understand solution to anything). I'm gratefull to have learned first foxpro. Is my secret sauce to be better with sql databases. I kill to have linq in other languages. How I wish to have learned haskell before. How I hate the verbosity of anything except python. Why python have null exceptions? And so on.
Is not, I think, the true realization of many, claimed to be: "shades of gray" but instead: Look, understand the life, universe and everything else become simpler when we have telescope, microscope, radioscope, math, physic, art, music, literature, science fiction, the other boring but useful science...........
Getting literal again, if you think static typing is a PITA, you probably haven't used a modern language with type inference. There is really no downside to it anymore.
Dynamic Typing: Crap, did I really miss that?
I think a lot of times features of languages are highlighted because they make it easier or faster do write code. And so it should be. But a huge factor in this is IDE support. Those people sticking to their various flavor of the month text editors don't understand this.
Here are some examples of my own work: I used a lot of java, and its annoying that everything needs to be defined and if you change the definition, there is a large amount of work to do. And takes time to compile. This would be terrible except if you use an IDE, for example Eclipse: Eclipse has an iterative compiler - it keeps your entire project compiled at all times. If you change a line of code, the iterative compiler analyzes what must be re-compiled and does that in the background. This is so fast you wont ever notice it. The effect is that there is no compile step, ever. Compiling is a non-issue. Same for strong static typing: The IDE allows one to just write code, like a = "foo" and deal with the consequences later, it will ask you whether you want to define a as a local or instance variable, or if it already exists if you want to change the type to string. Then it makes all necessary adjustments across the entire project. De facto you have the advantages of both strong and weak typing.
The IDE therefore is very effective at eliminating perceived weaknesses of languages, with code generation, inference, refactoring, intention guessing etc.
Another example is Objective-C. I was about to lose my mind with the sheer verbosity and redundancy of this language, then along came AppCode IDE and removed all the problems.
Its like there is a pure programming language out there - Ruby is probably closest - which allows one to express code most succinctly. The most expressive pure language. But all the others can be made to be very close to that with IDE support. Its not a co-incidence - computers can automate all mindless, repeating tasks and if you take all those away from any language what is left is pure logic.
Which is interesting, there are actually 3 issues at play: 1) Dynamic languages: slow runtime cost, but expressive and fun to work with 2) Static (simple) languages: fast compilation, but soul draining 3) Static (complex) languages: slow compilation, but expressive and fun to work with.
Here are examples of 1,2,3: Ruby Java Scala
I'm in the latter camp, thus the hurry up and compile! ;-)
[C++ is something of a red-herring, I think: although it's one of the most famous statically-typed languages, and has a reputation for slow compilation, that has much more to do with its include-based model than it does with static typing.]
I've thought that present web development is in constant flux, as businesses figure out how to get their data online; frameworks etc are also in flux. And so dynamic typing is beneficial. Then, once we've established the best way to do things, static typing will again become important...
But it could be that development - change - has accelerated, and will only get faster. In which case, whatever gets you to working, to iterating and adapting to change, will be favoured. Which is a pity, because I like static typing.
Why is Smalltalk (an archetypal 'dynamic' language) so darned easy to work with, and (very importantly) get right?
Is it coincidence that much research in modern programming languages and concepts is being conducted using Smalltalk-like environments? (Examples: http://scg.unibe.ch/research/helvetia, http://www.viewpointsresearch.org/)