The Long-Term Problem with Dynamically Typed Languages
chadaustin.me
chadaustin.me
Invariably, the ugliest code I've seen has always come from "we must move fast at the expense of everything else" startups. I worked at a startup that combined the worst of architectural temple building with the worst of "maintaining code is a Maserati problem" (e.g. by the time somebody has to maintain it, I'll be driving a Maserati and not caring). The company almost folded due to the weight of the code base preventing features from being released. Did I mention this was all Java?
Many fast-moving startups choose dynamic languages to do their ultra-fast, sloppy iterations on. Fast-moving startups tend to write code that is unmaintainable. Later in the lifecycle, as the maintenance nightmare hits, these same companies move to static languages to "fix their problem", but they've also introduced a lot more discipline in their engineering process. Afterwards, the dynamically typed language gets the blame for the lack of maintainability and the static language gets props for being maintainable.
Correlation is not causation. The engineering practice has far more to do with maintainability than the language itself. It just happens that, in the last 10 years, dynamic languages have been chosen more often during periods when companies write poor code. My Java example above was from 1998 to 2001 , when Java was the hot language for building the dotcom boom.
Sure, but not all unmaintainable codes are equal.
Code without types is orders harder to maintain and modify, even with extensive code coverage (because you can never really now how extensive that coverage is).
Also, the absence of type annotations make the simple human understanding of such code extremely harder.
Really, the main point I'm trying to make is that when you have millions of lines of code spanning a decade, it's WAY easier to have a compiler guiding you through certain types of refactorings than a test suite that takes half an hour to run. (If you search the internet for things like IMVU tests buildbot continuous integration you'll see how large our test infrastructure is.)
Our newest language at IMVU is Haskell, and the earliest Haskell written was quite nasty. We hadn't figured out the structure we wanted. But it's shockingly refreshing to be able to lean on the compiler to quickly restructure all of that old code.
I guess my point is that we can be pedantic, but each one of us will probably have a different interpretation of the terms.
The academic literature uses types to mean checked at compile-time, and tags to mean checked at runtime. Python, calls them types but checks at runtime. I think the fuzzy terminology can work as long as we all declare in advance what we mean. :)
In general, I agree with your points. Where strong type systems start to help a lot is when you have super-complicated data flows and invariants, where many different types might actually have the same representation (say, a 32-bit integer or small string), but only some operations are valid on some types. That stuff gets tricky in Python, even if you implement a unique class per type.
So, dynamic strong would be Lisp[0], dynamic weak would be PHP, static strong would be Haskell, static weak would be C.
I suppose there's even a fifth option of "no interpretation semantics to begin with", which would be something like Forth.
[0] As you seem to allude: a dynamic language makes this question more tricky.
Now, this is in large part because static type checking is a rather expensive operation. The "price" of which has been dropping significantly with computers. But, I can't help but see a large amount of hubris in the field of people proclaiming that dynamically typed languages don't lead to solid solutions.
Especially when some of the larger train wrecks I have ever had the pleasure of working on were abuses in higher kinded types.
The bottom line is that statically typed languages (STL) have been much better at getting good at what dynamically typed languages (DTL) shine than the other way around to the point that there is hardly any good reason left to use a DTL.
These days, STL are terse, fast, expressive, are supported by tremendous tool chains including amazing IDE's and DTL pretty much suck in all these areas.
Seriously, think about that example for a minute. During a time when statically typed languages were getting more and more tooling than you can shake a stick at, browsers take off and prove that the ultimate tool for any language is a widely distributed runtime.
So, where is the difficulty in seeing that dynamic runtimes that can adapt to changes in the system have a very valid place in the future? I realize I'm an emacs person, so biased as can be. But there is something inspiring about finding a problem in my editor/runtime, popping over to the function, fixing it, reevaluating it, and then continuing without a restart. Is ridiculously nice.
Javascript still reigns supreme, no argument there, but the contenders that are trying to replace it all have something in common: they add types to Javascript. All of them.
I think the trend is pretty clear.
And, correct me if I'm wrong, but Haskel and vanilla Java, along with many other common statically typed languages, can not hotswap. Well, to be fair, vanilla Java can, under limited conditions.
Ruby and Python were the latest victims of this trend, Javascript will clearly be next, even if it might take years.
I mean, ultimately, I think I agree with you. But at the rate things are moving, I expect my children will still learn JavaScript. And probably C.
Millions of people want to write programs that runs in a web browser, and it's easiest to do that in JavaScript.
JavaScript is a textbook example of "worse-is-better", and you are right, for compatibility reasons we are going to be stuck with it until long past its best-before date, just like C.
I only recently discovered JSDoc, and it goes a long way towards documenting types when they matter, especially if your IDE/editor (e.g. - WebStorm) recognizes the annotations and displays them when you click on a symbol.
Now I must "reflect" upon the wisdom of all the strong typing in my Java programs, and how a simple refactor always safely finds all the use cases of a class/method/variable for me. (NOT -- although IntelliJ is pretty good at finding many common hiding places in various texts for program identifiers)
I really want one language that combines optional (dynamic) and mandatory (static) compile-time type info, with some pragmas at the top of a module warning me that the module uses or supports dynamic types, rather than having to resort to XML DSL monstrosities.
One final rant: The author mentions an FP language (Haskell), and then talks about data flow analysis. Strangely, however, he seems more concerned with types than immutability and explicit data passing / folding. Favoring explicit input/output values over side effects, is a huge factor in seeing data flow (and orthogonal to compile vs runtime types). Alas, the "enterprise" development squad seems to actively resists learning FP, preferring to cling to "COBOL with namespaces and compilation units" as state of the art programming methodology. I think this problem is the real (productivity) killer.
(OTOH, he could tacitly assume pure functions, etc, are a good thing, and should be the norm. Hard to say)
The author runs into a few logical fallacies like underestimating how important early agility is for making a product successful (if your product isn't successful there is not much to 'fortify' using static typing) or conflating static/dynamic with strong/weak.
The bottom line imho is that we want languages that are both easily toolable (type systems can help here) as well as allowing us to do fast prototyping. It seems like starting with a dynamic language and to add optional/gradual type annotations is the way to go.
On the top right you can select a Dart code sample. DartPad is similar to jsfiddle but it gives you semantic autocompletion, warnings and auto-correction like IDEs for languages with static typing would do.
Note that I'm not at all saying that every statically typed language is superior to every dynamically typed language. Many - particularly older, popular, and oversold statically typed languages - asked for a lot of boiler plate in exchange for not terribly valuable guarantees.
What I meant was the ability to write new code and see if some functionality of the product 'works' before I have to satisfy the type checker. In statically typed languages I have to satisfy the type checker before I can run the code, that is a barrier.
Regarding refactoring good tooling supported by type annotations are definitely helpful. I guess what I'm trying to say is that we have languages now that give us the best of both worlds, no more need to pick either extreme of the spectrum or use a dynamically typed language in the beginning and to rewrite everything again in another language later.
Let's go too far, and define "agility" to be "things that make me faster in the first 5 minutes working on a problem."
I am saying that when I am writing Python in that context, Haskell's type system is something I find conspicuously missing.
That's not true. It's trivial to represent Python's "type system" in Haskell. An incomplete example of what that might look like:
data Value = None | String Text | Dict (HashMap Text Value) | List [Value]
Also, consider things like the very useful Data.Dynamic: https://hackage.haskell.org/package/base-4.3.1.0/docs/Data-D...On the importance of early agility: I'm actually 100% in agreement that early agility is critical. In fact, I directly cite Bruce Eckel's arguments to that effect. However, if a business is going to remain agile in the long term, it should probably invest in "correctness tooling" sooner rather than later.
A lot of these languages make for lovely blog posts and also happen to be fantastic for gluing things together. But I fear there's an entire generation that's unaware that C# looks like a lot of boilerplate in a textbook but actually requires fewer keystrokes than most equivalents. And once you get fast at it, it's pretty amazing.
I use lots of Python. And Matlab. And of course Javascript.
But as you get older you realize that tools matter. And the tooling around dynamic languages by definition cannot beat what's possible when you declare a more rigid contract of what you're actually trying to get the machine to do.
Games are an interesting example because of 1) strong tooling requirements 2) small but diverse teams 3) cross-platform requirements and 4) performance requirements.
Under these constraints they made their choices. And it wasn't because Python wasn't available.
I wasn't arguing for unit types (user-defined literals didn't appear in C++ until C++11...)
I was pointing out that reconciling units in physics is common sense. People don't debate whether writing down "meters" or "seconds" next to a quantity is useful or not. You just do it, then cancel the units as a basic sanity check.
Yet so much of the "strong typing is for people with weak memories" debate incorrectly centers around the equivalent of "len = 3.1" vs "Kilometers len = 3.1" (or, heck, "auto Distance = 3.1_km").
I'm not sure why people think this is some sort of ordeal but my hunch is they'll learn. Timezones, Unicode, Database Schema, Float vs Decimal: you can deal with it now or you can deal with it later. If there were a lightweight language that actually solved this we'd all be using it, not arguing about it.
For type systems, we really need much better type inference than we have now...something that works with the subtyping that many of us prefer to work with.
Enjoying that economic windfall while doing what's all but scientifically provable as a half-assed job raises issues around quality and ethics. (Something as a community we're also a bit famous for tiptoeing around...)
In addition to Java, I regularly write Python, Ruby, and Javascript using IDEA. The code navigation and code completion functions in those last three are utterly hopeless and fallback to grep-like behavior most of the time. Ruby deserves special mention because pretty much anything can get redefined at anytime anywhere. The poor IDE doesn't have a chance.
Now, in dynamic languages this is typically done at runtime. That is kind of the whole point of dynamic runtimes; that you can hook into them. Curious what all properties are available on the object represented by a given symbol? Essentially, ask it.
However, as computers get more and more powerful, this can be done by simulation statically for many programs. No, not all. Just many. Is it enough, though?
I need to write code that talks to libraries today. I press dot and within 10 milliseconds I see an encyclopedic list of functions, complete with verifiable hints as to their structure. I type what looks like it makes sense, run the compiler that takes as much time to compile than python.exe takes to load, and a lot of the time, if the code compiles it just runs perfectly.
I do not need to wade through verbose Doxygen boilerplate, nor page through source code, nor dance around fake json schemas. I do not need to write unit tests that, in many cases, serve merely to exercise the interpreter to make sure it hits everywhere I might have forgotten a parenthesis. It just friggin' works because a community has decided it's important to do things this way.
So many of these "time-saving" languages are a full decade behind this reality. Right now it's definitely not enough. There's 30 years of idiotic cruft behind Windows bitmaps and it's STILL faster and easier to resize a jpeg in C# than using the insane Python libraries.
Meanwhile C++ got "auto" and C# got "var" and "dynamic." At that point I picked my winners, there are of course other options.
And when I write a library, doing it this way just seems like basic human respect. Sure: you're welcome to the source and here are some lovely docs. But if the only way to say whether something takes a string or bytes and that it varies between Python 2.7 and 3.x is in a README file, something's gone wrong. Like environmentalism we need to look at "total cost" before we declare something a net savings of energy.
Which is not to say it is bad that you do this. Just, to each their own. I'm much better served by working before getting to the keyboard. Other folks aren't.
That is, the inverted method would allow a way to say what all has to be on any object that is passed to you. If they have more, so be it.
Most of the time, the prototype is good enough for production. If not - there are implementation languages (C, C++).
So, I cannot see any problem.
If you don't choose static typing early, you might never be able to again.
schiptsov@MSI-U270:~/arc3.1$ du -sh .
472K .
Anyway, at least for me, static typing is the must is a marketing meme from the time of Java madness.And if I really need it I could re-write my prototype in no time (like translating a ready book from one language to another).
Don't know how this very site has been made? Ignorance is strength.
That being said, I say use what works for you. Dynamic typing is obviously a viable solution, and there are plenty of successful projects that use use dynamically typed languages.
However, after almost 10 years of using dynamically typed languages I can tell you that I no longer believe the trade-off is worth it. Even on small single person side projects, I personally think that after a thousand lines of code or so, the benefits of static typing outweigh the cost.
OK, they reused mzsheme's runtime, because runtime is hard (even clojure delegated everything to JRE) but nevertheless, Arc and this site are remarkably successful examples of real world project with all the benefits of dynamic languages.
Sorry for the delay, we had a quake that day.
Is there any empirical evidence to back this up?
And if I didn't mentioned Golang It doesn't mean that I am unaware of its existence.)
Look, you don't need every single type that C implemented to have a type system. You can just have number, w_string, w_char, byte, object and maybe DateTime as core types. Then build on those. But typeless languages are just maintenance nightmares that just get worse as time moves on. They are also inherently incompatible with any system which does use types (which is most), like databases, XML, libraries, OS APIs, and so on.
I know I am going to get stomped into the ground for this, but JavaScript is bad and I wish we had something, anything, else instead. JavaScript was made in like a weekend and copied Java badly, that's fine, but we've carried this legacy long enough and the longer it takes us to implement the replacement the longer we'll have to keep using JavaScript until every browser/device catches up (10-20+ years from now).
There are many things that could replace JavaScript, but all I want is a strongly typed language, with real classes, strong focus on libraries, a MUCH better core DateTime library, and with maybe some functional programming concepts to make it more maintainable and more optimizable (at runtime).
PS - I am aware of ECMAScript 6, but it is only a bandaid on a bad language at the core.
PPS - Dart is already dead. And "X converts to JavaScript" is also a bandaid.
I can't load the article, so I have limited context here, but note that being dynamically typed is orthogonal to being weakly typed/typeless.
This is obviously untrue in practice by any usual definition of "incompatible", as all of those things can be -- and are -- used successfully from languages everywhere on both the strong/weak and static/dynamic axes of typing.
Please, go ahead, and correct my ignorance. Since I don't know anything...
> It doesn't replaces JavaScript in any way
Except for the important place, my code treeJavascript has pretty much nothing to do with Java. It's not a copy of Java in any way. The semantics of it are vastly different and much more influenced by Smalltalk, Self, and Lisp. It's an extremely functional language, which is one of the things that has ultimately saved it. Without higher-order functions, you don't have jQuery, Underscore/Lodash, AMD, Backbone, and all the other innovations that made using JS not suck nearly as much.
Don't get me wrong, it's a pretty flawed language. But "something better" is practically here now, in the form of ES6 and TypeScript, and to a lesser extent, Google's "use strong" proposal, which basically deprecates a whole bunch of old cruft. We're about a year away from being unshackled from the worst aspects of JS. You can pick TypeScript or Babel today and have many of the best benefits of future JS.
I never said it was. I said:
> JavaScript was made in like a weekend and copied Java badly
The original author knocked out JavaScript's overall design in a weekend, then they tried to shoehorn in Java-like features badly. That's why we have artifacts like the Date() having month at 0 (like Java). They took a whole bunch of Java concepts and tried to clone them into JS after the naming.
My post would have made more sense if you understood a little more about JavaScript's history (both before and after naming). There was definitely a sense of making it more Java-y after it was named that.
> It's an extremely functional language
No, it just isn't. JavaScript has state all over the darn place. I mean heck it has a global scope with no namespaces at all, that alone is a death nail on calling anything a "functional language." I have no way of writing a function in JavaScript which makes a guarantee that it has no internal state, so the optimiser cannot make assumptions about that function either (e.g. inlining it). In fact I can dynamically change a function's definition at runtime, so it never runs the same code twice.
I suppose "functional" is in the eye of the beholder. I see it in terms of what a language allows you to do, not what it prohibits you from doing. You can absolutely choose not to use shared mutable state.