Have Static Languages Won?
pointersgonewild.com
pointersgonewild.com
Patently false. * from Wikipedia
Python: Guido van Rossum * Master's degree in mathematics and computer science from the University of Amsterdam * Recognized as a Distinguished Engineer by the Association for Computing Machinery
Ruby: Yukihiro Matsumoto * Graduated with an information science degree from University of Tsukuba, where he was a member of Ikuo Nakata's research lab on programming languages and compilers
JavaScript: Brendan Eich * Brendan Eich received his bachelor's degree in mathematics and computer science at Santa Clara University. He received his master's degree in 1985 from the University of Illinois at Urbana-Champaign.
Perl: Larry Wall .... Well I guess he doesn't have a formal CS background. But he was literally a NASA rocket scientist. https://en.wikipedia.org/wiki/Larry_Wall#cite_ref-3
Lua: Roberto Ierusalimschy * Associate professor at PUC-Rio.
Tcl: John Ousterhout * Professor of computer science at Stanford (and professor of computer science at Berkeley when he invented Tcl).
Plus, Python's design was based on a teaching language (ABC) that was designed based on actual usability research.
And, of course, this does not even count the classics (LISP, Smalltalk, Scheme) whose authors have pretty impressive academic pedigrees and whose designs inspired many of the more recent languages (e.g., the influences of Smalltalk and LISP on Ruby are pretty obvious).
Don't forget the other dynamic language frequently mentioned in the article...
PHP: Rasmus Lerdorf graduated in 1993 from the University of Waterloo with a Bachelor of Applied Science in Systems Design Engineering.
But perhaps the author's definition of "mainstream" includes languages of Groovy's degree of adoption.
Just because you don't have to explicitly write out the type does not make it any less static.
I believe that the usual arguments against languages with static typing such as verbosity and lack of flexibility are largely eliminated by the more modern languages such as Scala, Kotlin or Typescript (though typescript is more "optional typing" than static really).
No, but not having to write the type was one of the biggest attractions of dynamic languages -- a lot of us didn't care that much for being fully dynamic, but just enjoyed the less ceremony of dynamic languages.
As for not having to commit to types for some cases, STL languages can also have a "void *" or "variant" or "dynamic" type (a la C#).
As an example, duck typing is usually not possible to achieve in a static language - it's really hard to allow duck typing and remain sound (I don't actually know that it's possible, but I'm not sure either way).
Having said that, I'm firmly of the opinion that static languages are much, much easier to write in and maintain once you get the hang of whatever type system they use, and the loss of completeness is acceptable for all the benefits they bring.
I see what you mean, this feels like a static version of duck typing. I'm not sure how I feel about loosing control over what supertypes my type extends, though - well, I lie. I don't like it. But I haven't given this enough thought for a constructive, well argumented discussion on the topic.
Well, don't you lose that by default (and even more) with dynamic typing?
I do believe there are cases where dynamic typing is genuinely useful, but those make up a very small portion of real-world problems.
A couple of years ago I worked for DARPA project that was building a system designed for greatly enhanced security and reliability. It had a novel programming language designed to be able to express a rich set of reliability and security constraints in the type system. It had a rich static type system, but it also had a dynamic type system. Why? Because some important constraints simply can't be analyzed statically.
For example, a program could say that it was impossible for a given piece of information to be delivered to an unauthorized receiver. That constraint cannot be analyzed statically because the authorization status of a given agent can change at any time. The type system had to be able to check authorization, so it had to have a dynamic checker (in fact, the system was designed to be able to do dynamic checks in hardware).
Maybe this sounds like an esoteric use-case, but is that because nobody wants to be able to express such constraints, or is it because we just don't expect to be able to do so because we haven't had the tools?
The project in question is in part meant to make the case that such use-cases should not be esoteric--that one of the reasons that our reliability and security situations are so embarrassing is that we haven't so far taken those considerations seriously enough to develop the tools to deal with them. If we want to get better, maybe we should be thinking about how to integrate both static and dynamic type systems, rather than exalting one and denigrating the other.
On the other hand, I disagree with the scenario you describe: if you have multiple types that come from an external, closed-source library, you won't be able to have them extend a new interface. If you're lucky and they've not been marked final, you'll need to wrap them in another type that extends the right interface, then unwrap them... not at all impossible, but a lot of boilerplate.
The best solution I know of is type classes, in languages that support them.
If you have a function that expects a type A, and you want to pass an object of type B that doesn't have A as a supertype, I don't know of many ways to do so. Either you wrap your B in a type that extends A, or you create a new implementation of B that extends A (this is often the same solution as the first one), or you use type classes or some variant of the concept. Is there an other option that I'm not seeing?
They allow you do something else, something I would argue is better (and I explicitly said so, "The best solution I know of is type classes, in languages that support them."), but are entirely irrelevant to sanderjd's point and my answer.
But, yes. The more I code, the more it feels like type classes are never not a good answer.
"The more I code, the more it feels like type classes are never not a good answer."
As they are used in Haskell, they sometimes get awkward where you where multiple instances are relevant to the same data in the same scope. You can newtype cast around, but I've been wondering (with multiparameter typeclasses) if reifying the instance we care about is 1) useful, and 2) actually still distinct from explicitly passing the dictionary as a record. Regarding 1, I've consistently leaned "yes", although without tremendous confidence. Regarding 2, I've gone back and forth over time.
The way I understood that is, if you have a f(a: A), and need to pass it a B and C, neither of which are subtypes of A, you can just create a new type D, have A, B and C extend D, and modify f to expect a D. My point was that it's not always practical nor even possible to do so.
A type class would be a good way to abstract over the type itself and simply require a specific behaviour, but by the time you have f(a: A) where A is a concrete type, and you can't modify f (because it comes from an external library, say), it's too late to retrofit type classes into it.
As for your other point - type classes being awkward when you have multiple possible instances for a given type - I agree, but must say that I'm spoiled: my primary use of type classes is with Scala, where they're just, when you come down to it, parameters to functions. You can let the compiler infer them when there is no ambiguity, but when you need to pass an int to a function that expects "A with a monoid", you always have the option of being explicit about which monoid instance to use. I believe Haskell treats them as implicit parameters as well but without the option to pass them explicitly.
Finally, I'm afraid you've lost me with your last bit - reifying the instance we care about and passing the dictionary as a record. I wish I had something smart to answer, but I'm not sure I actually understand what it means. I know enough Haskell to be able to be able to read it (a necessary skill if you have even the slightest interest in FP), but not much more than that. Maybe if I knew more I'd have a clearer understanding of what you mean?
I agree with your starting point, but to my mind type classes are precisely a way to "make [types] implement an interface". I completely agree that it is often impractical to do so by defining new types.
I'll address the rest when I have a little more bandwidth - I hope you'll forgive me; between interesting projects at work, a new baby, and holiday goings on, attention is in high demand.
I understand that we're not arguing, and find the conversation interesting: I approach it from the point of view of someone who was mostly taught OO (and learned whatever CS theory he knows by himself), where you seem to have a more solid FP and theoretical background and entirely different viewpoint.
As a new father myself, I absolutely understand that free time is scarce. Enjoy your baby and holidays!
"Passing the dictionary as a record" is a pretty common alternative to typeclasses. The way it works is if you have a typeclass:
class Foo a where
foo :: ...
bar :: ...
you instead make a record that looks like: data Foo = Foo { foo :: ..., bar :: ... }
This works great for a lot of things, and is often preferable to typeclasses when all you're doing is defining an interface. One objection is that things look slightly less tidy, but the main problem with it (where it's a problem at all) is that there's no way of identifying individual implementors of an interface by type anymore. An example where this relevant is Set. We could define the Set operations to take a comparison function, but we couldn't be sure that all comparison functions agree across all interactions with a particular set.What I mean by "reifying the instance" is indexing typeclasses with a (single constructor) type that identifies the instance, and then passing that around to let us select which instance we care about, even for the same underlying type.
For example:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
import Data.Function (on)
class ParamOrd o a | o -> a where
pcompare :: o -> a -> a -> Ordering
data StringsOverLength = StringsOverLength
data StringsLexicographically = StringsLexicographically
data IntegersInOrder = IntegersInOrder
data IntegersReversed = IntegersReversed
data ReversedOrdering r = ReversedOrdering r
instance ParamOrd IntegersInOrder Integer where
pcompare _ = compare
instance ParamOrd (ReversedOrdering r) Integer where
pcompare o = flip (pcompare o)
instance ParamOrd StringsOverLength String where
pcompare _ = compare `on` length
instance ParamOrd StringsLexicographically String where
pcompare _ = compare
Now we could write a new version of Set, `ParamSet o a` such that we can still enforce agreement while choosing our orderings with an argument rather than a newtype.I went around in circles and ultimately concluded no amount of type inference or other things in Scala's bag of tricks would let a user of the API avoid the dreaded type cast.
I have often seen this situation whenever I have tried to write generic code in a static language that needs to interact with a database.
They are both "limited" to being Turing complete and therefore should be able to express the same things.
Implementing the same algorithm can be cumbersome with types at times (No 1:1 representation of JSON Objects for example) but you can define types which wrap things like that to make it work anyway.
Including a static one with a "dynamic" type like C# has?
What would that be?
Even Haskell has Data.Dynamic, though it's not often used.
Especially if actions are restricted to those valid on all parts of the union, I'd say it is correct to consider that to still be static typing.
The author seems to paint a broad brush in saying all dynamic languages are poorly designed. Languages can have different design goals.
From Matz, Ruby's creator;
> Instead of emphasizing the what, I want to emphasize the how part: how we feel while programming. That's Ruby's main difference from other language designs. I emphasize the feeling, in particular, how I feel using Ruby.
( http://www.artima.com/intv/rubyP.html )
So one of Ruby's primary design goals was programmer happiness, and in that respect it isn't not poorly designed at all. The technical 'perfection' of a programming language only matters to a point, it's us as programmers people that have to speak its language all day, and optimising for how we feel doing it is a valid design goal.
I don't think it is a valid criticism to say something is poorly designed, without looking at its design goals. Every language is poorly designed in one way or another.
This is not about expressiveness (the article author is wrong here IMO). It's all about flexibility and (more important) interactivity. I can do useful things in ipython console or in ipython notebook in the browser. It's like a tiny lab for small and moderately sized things, data munging, analysis and visualization, prototyping and all this stuff.
There is dynamic languages like Python, R and even MATLAB that's not going to disappear anytime soon. There is a new dynamic language called Julia that's only starts getting traction but IMO it's already more interesting then Rust, Go and Nim combined together.
I'd think this sentiment misses a small bit of context.
In the above combination, Python is the glue language, which merely makes it easy to dispatch the heavy lifting. Numpy is a damn good convenience wrapper around the under-the-hood number crunching engines - which themselves are written in static languages.
I mean, have you ever tried to _build_ numpy from scratch? That thing pulls in both BLAS and LAPACK, some of the most heavily optimised libraries in the world. These fortran(!) libraries can trace their ancestry all the way to 1970's, and have benefited from aggressive optimisations done over the course of 4 decades. [0,1]
So, invoking numpy as a testament to power of dynamic languages misses a pretty important point. And I say that as someone who loves Python, and uses it as a first-choice tool in perhaps 90% of use cases.
0: https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprogra...
I don't think so. People build Numpy in a different language to the one they expose it in because those languages have different strengths.
If Numpy's source languages (including BLAS and LAPACK) were suitable for front-end usage, Python would have never entered the equation.
I think it's more a testament that despite the base libraries being so freely accessible, only dynamic languages have actually built front-ends that people want to use. Even Julia is dynamic.
Clojure & other Lisps, Python, Erlang, Lua, and Ruby are all application programming languages first and web languages only incidentally (1). They all have clear strengths and are miles away from the horror of JS and PHP.
I think it shows in the article that the author's thinking on dynamic languages is colored by Javascript and its propensity to hide bugs. This is worrying since it might signal that JS does this for many people :(
(1) Admittedly Ruby is more strongly associated with Web nowadays due to Rails.
In a way static typing can save you just like reading of documentation, but in a more organized manner. That said you should still eat your ve^W^W^W read documentation.
What helps in case of dynamic languages is REPL. It is even better than documentation sometimes. You can easly check every line with your reasoning. For C or C++ I always have some simple file for testing basic constructs to check my assumptions. One could argue that that is the role of unit tests, but one does not prevent the other. Unit tests are same thing, but in more organized manner.
a) Maxime works on a compiler for JS, so even if something is documented to death, it usually makes analyses less precise and/or more complex.
b) Many statically-typed languages have a REPL (OCaml, Haskell, Scala); this is not an issue with the typing discipline, but with the tools available to the developers.
Prelude> :t mapM undefined [5,6,7]
mapM undefined [5,6,7] :: Monad m => m [b]
This can get even more informative with Typed Holes: Prelude> mapM _whatFunc [5,6,7]
<interactive>:14:6:
Found hole ‘_whatFunc’ with type: Integer -> m b
Where: ‘m’ is a rigid type variable bound by
the inferred type of it :: m [b] at <interactive>:14:1
‘b’ is a rigid type variable bound by
the inferred type of it :: m [b] at <interactive>:14:1
Relevant bindings include it :: m [b] (bound at <interactive>:14:1)
In the first argument of ‘mapM’, namely ‘_whatFunc’
In the expression: mapM _whatFunc [5, 6, 7]
In an equation for ‘it’: it = mapM _whatFunc [5, 6, 7]All of which are established languages created in the 90's. It takes time for languages which are rapidly gaining mindshare to gain marketshare on GitHub.
Honestly curious: which new languages are those?
Same for Rust, which mostly replaces C++ and a bit of C. Maybe some Java, too, for people using Java for the safety despite really wanting something lower level.
IMHO Go isn't much competition to most popular dynamic languages, but some people disagree.
Because of Android.
2. http://www.php.net/manual/en/migration70.new-features.php
You will have to pry duck-typing from my cold dead hands if you try to remove it from PHP/Ruby completely.
Compared to CS students, the average web developer is not as passionate about programming, and not as skilled a programmer.
...probably takes the biscuit; I'm not even sure where to start with it really, apart from my complete failure to understand how "web developers" and "CS students" are supposed to be two distinct groups which are able to be compared.
I understand the distinction: CS students get geared up to understand algorithms, languages, and theory. A web developer understands his tools, his workflow, his practice, and people's needs.
In comparison with my battles to get something to work in both Firefox and IE6 a few months later in industry (in order to ship something that people's jobs actually depended on), it was an absolute walk in the park.
> He would crush me with a storm and multiply my wounds for no reason.
Seems appropriate for some "9-17" jobs.
I like your interpretation as well, though.
> He moves mountains without their knowing it and overturns them in his anger.
Other people came in after programming as a part time job for a few years. I think it's a little quick to say someone came in for the money because I came in not knowing a ton of programming. On the other hand if course I look at salaries when picking majors. Otherwise I would have been a sci-fi writer :) Programming is better than writing now that I learned it...
Given the timing (tail end of the last bubble), I always thought of the second group as "there for the money".
As with any generalization, it breaks down in plenty of specific cases.
This unfortunate conflation of "CS" and "programming" is so common it's starting to become the mainstream definition of CS, much to my annoyance, and soon it will no longer make sense to push back against it :(
I hasten to add that I hold no official position on the matter and merely wanted to help you see how it's possible to consider CS graduates and web designers as too distincts sets.
The mentalities have evolved. People know that you can drastically reduce the development costs (of small projects) by using dynamic languages. We know also that maintenance is more costly.
Static languages are always improving to support features that used to exist only in dynamic languages. Dynamic languages evolve to support optional typing. The differences are reducing, but I think there is still a need for the 2 kind of languages.
Often they are combined in big projects. For example c++/javascript in browser or c++/guile.
There is a lot of interesting research into strong static type systems but that has been going on for ages (ML family of languages).
In fact, I'd say if anything there is a big surge every decade or so in dynamic languages when a new problem space emerges.
90s - Perl, Javascript
00s - Ruby and Objective-C resurgence
10s - ES6 and JS related languages
Once an area matures, the paranoid static type people come in and harden things. Then people realize they're getting nothing done and add DSLs on top for flexibility. And on it goes.
Ada users are paranoid and Java users are concerned about project cost.
I see the project going one of two ways. One all loosy goosy. One all statically typed. At which point what did optional typing add? Pick a side and go for it.
There's also the commercial aspects of static typing in a dynamic language. After 10 years, Groovy introduced the annotations to optionally statically compile code in order to compete with Java instead of complementing Java, which changed the business purpose of the language and their relationship with Oracle.
For example, this quote from the conclusion: "The possibility of standardization of computer languages is still remote, and in the author's opinion will remain so until standardization in hardware applied to standard problems is first achieved"
Are we there yet?
It does rely on systems written in static languages and is itself written in one... but dynamic languages are very useful for a class of problem where deferring decisions about specifics until run-time is a huge advantage. Normally this is reserved for exploratory programming but in practice it is also useful for dynamically configured systems that can introspect themselves at run-time and change that configuration or adapt to unstructured input.
Update: I'm also one of the unwashed masses that, gasp, didn't go to university and study PLT. I still have a dog-eared copy of SICP, The Dragon Book, Concrete Mathematics, et al... and started by making terrible pong clones and text adventures in BASIC on an Amiga 500.
That doesn't say much in itself though. Such infrastructure for "earthquake notification" could just be a few thousand lines or even mostly "glue" code.
As you say "It does rely on systems written in static languages and is itself written in one" which kind of refutes the whole argument, doesn't it?
>It does rely on systems written in static languages and is itself written in one... but dynamic languages are very useful for a class of problem where deferring decisions about specifics until run-time is a huge advantage.
For those cases couldn't one just use a DSL or at worse an sandboxed isolated dynamic language from inside a STL talking care for the rest of the system?
There are several "business rules" systems for STL.
https://github.com/openstack/neutron
I assure you that OpenStack is a little more than "glue" code.
Not sure what part of OpenStack is written in a STL, but the vast majority of its hundreds of thousands of lines of code is python.
In total Openstack encompasses around +4M SLOC written in Python. How much is deployed in a given cloud depends on the configuration. However the community must still shepherd and manage all of that code and make regular, stable releases on a six-month cadence.
It's not "just" glue code.
> As you say "It does rely on systems written in static languages and is itself written in one" which kind of refutes the whole argument, doesn't it?
Not necessarily. Awk was necessarily written because querying unstructured input is a huge pain in plain C. Kernighan gave a recent example of this in one of his recent talks on programming language design.
The caveat is there is a limit at which adding more lines to an awk program becomes counter-productive. It's essentially a good DSL but a terrible language. I think Python defies that -- with the millions of lines of code managed by the community of thousands of developers a large system written in a dynamic language isn't impossible or necessarily hindered by its lack of static type analysis.
> For those cases couldn't one just use a DSL or at worse an sandboxed isolated dynamic language from inside a STL talking care for the rest of the system?
In some sense this is how I view Python and languages of its kind... but I think in practice it's more of a philosophical viewpoint. In practice dynamic languages are general purpose and much suited to certain kinds of programming that static languages are not.
Common Lisp and Smalltalk are great examples of dynamic languages kicking butt. Starting in the debugger and developing self-introspecting systems has a peculiar effect on the programmer. One tends to develop a domain language for solving a class of problems in order to make them tractable. One thing I dislike however is how divorced it can be from the machine. Some Common Lisp compilers can produce really awesome code but it does take a little more burden from the programmer to gain such efficiency from the compiler (compared to the tactical omissions of defined behavior from languages like C which allow almost trivial optimizations... with the trade off that certain errors are quite likely on my part if I'm not careful).
I think there is room for both in the world. I really enjoy OCaml, C, Python, and Common Lisp... and I would seriously consider the problem before the language. Our job after all is to transform data and not spend time writing pretty, elegant, or clever code for the code's sake.
update: grammar.
I'm not making any representations as to whether or not this is true of OpenStack. I honestly wouldn't know.
Any suggestion that Python code cannot possibly execute without it is an exaggeration.
I believe the biggest advantage with a good static type system is that programmers can design good datatypes and specify their relationships - and the compiler can then detect any inconsistencies(ideally anyway).
I do use a interpreter with both scala and python a lot.
It's also interesting to compare Python and JavaScript. Holkner and Harland did a pair of papers about those two communities' usage of dynamic typing - JS people do type changes throughout the program execution, but after initialization the Python people follow the Aycock quote: "Giving people a dynamically-typed language does not mean that they write dynamically-typed programs." Languages have different characteristic programs, to some degree.
I don't doubt that lots of new static languages have popped up, but how many people actually use those? Plenty of people use static languages, true, but those are mostly the old ones: Java, C, C++, C#, and Haskell and Scala.
Any claim that this has been settled by some new languages nobody is using is bizarre.
There's just not much point in creating a new dynamic language.
Ruby is flexible enough to turn into a DSL for whatever. Python does it's job very well. Lua has it's niche. Javascript is everywhere now.
What can a new dynamic language do that isn't easier to accomplish with libraries?
So most of the language development has gone towards making static typing easier to use. Improving on C++ / Java is easy. There are interesting ways to go about it with no clear right answer.
Javascript is the last serious holdover of the DTL generation but even it is showing tendencies to be replaced by STL (Typescript, Dart). It will take a while, though, but the trend is clear.
Ultimately, I guess it was easier to add lambdas to Java than to add solid IDE support to javascript.
[1] http://pointersgonewild.com/2012/11/09/static-vs-dynamic-why...
Personally I developed most of my web apps in C# but I find Python great for prototyping and data analysis tasks. Why is there a need to declare a winner?
The real appeal of what we today call "dynamic languages" is leaving type-checking up to the programmer, so in essence all programs are typed - just so happens that some don't formalize it inside the code.
A good compromise until then is gradual typing.
But people are starting to appreciate static typing assurances and IDE conveniences.
[1] http://redmonk.com/sogrady/2015/07/01/language-rankings-6-15...
[2] http://www.tiobe.com/index.php/content/paperinfo/tpci/index....
In real world? Php, Python, Ruby and Javascript still reign supreme.
Mobile is another story since main platforms are controlled by corporations, but if it were for the hobbyists neither java nor swift would have had a chance.
I'd more argue it's a sign that C and its derivatives are ripe to be replaced.
It's absolutely not that specific static languages have "won". But rather that the latest crop of static languages have proved that the oft-cited downsides of static typing, e.g. type stuttering in declarations, can be pragmatically addressed by other means, e.g. type inference. As such, the cost differential of static languages over dynamic is reduced to near-zero, and all of the benefits remain.
I don't really see an argument, or really counter-argument, in the linked post. The author complains that the current crop of dynamic languages are badly implemented, but I'm not sure that holds water when you look at e.g. Clojure. They claim the chief advantage of static languages is IDE support/integration, but that seems like a strawman to me, a fringe benefit of the real, structural advantages of an explicit type layer.
Meanwhile, dynamic languages like Python are adding type hints, to give many of the benefits of static typing while retaining full dynamic semantics.
The lines are getting blurry. IMO a good sign.
Java collection signatures are not super complex but they have fundamental bugs fixed in the slightly more complex scala collections type signatures.
compared to list.sort(function(a,b){return comparisonResult}) that was very demotivating to me.
https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headline...