I should note that author added some unneccessary cons to static types.
He said that metaprogramming is hard. The area of metaprogramming is partially covered by higher-order functions. The coverage is big, in my opinion, I haven't missed metaprogramming in Haskell for ten years, at least (I missed it for first two weeks with Haskell).
He also said that there's no mechanism to compensate for the absence of duck typing. In Haskell, at least, there are type classes which allow you to make your types as duck as you would wish, maybe slightly less. I think that some Scala mechanisms provide that power.
> He also said that there's no mechanism to compensate for the absence of duck typing.
Whereas, in reality, there's a whole class of static type systems which do the same thing, except statically: structural type systems (example: OCaml's object types).
Much as duck typing, they only make sense for object-oriented languages. Structural type systems treat (concrete) objects and object types as method sets (where a method is a triple of name, argument types and return types), where, given the types A and B with A a structural subtype of B, A's methodset will be a superset B's methodset.
> I think that some Scala mechanisms provide that power.
"He also said that there's no mechanism to compensate for the absence of duck typing. In Haskell, at least, there are type classes which allow you to make your types as duck as you would wish, maybe slightly less."
I think that type classes are the way to go.
Duck typing is mostly popular because multiple inheritance sucks, and
because inherting a class to only overload a method sucks.
Languages like Ruby, Python or Perl are so popular because they are less
verbose than Java or C++, with the disadvantage, that the compiler
can't help you that much.
Haskell is a language that combines the advantages, less verbosity with
a helping compiler, and a static type system that rocks.
While this is a statement in some ways of preference it is a reasonable comment; not sure why it's getting downvoted.
However, can you clarify why you would argue for type classes over duck/structural typing? You indicate that 'type classes are the way to go' but don't explain why.
Scala of course does have a form of Type Classes and while they tend to be what I reach for more often than Structural Types in Scala, it doesn't obviate Structural Types completely and I'd be interested in analysis of the diffs.
"However, can you clarify why you would argue for type classes over duck/structural typing? You indicate that 'type classes are the way to go' but don't explain why."
Like kscaldef said, you know at compiletime if something
can quack, and the overhead for doing this is quite small.
instance Quacker MySpecialDuck where
quack = "qqquuuuuuaaaaaakkkkk"
"Scala of course does have a form of Type Classes and while they tend to be what I reach for more often than Structural Types in Scala, it doesn't obviate Structural Types completely and I'd be interested in analysis of the diffs."
In which cases do you need duck typing? Adding dynamically
a method to a class?
Actually, any comparison between dynamic and static typing is prone to bias, only because such comparisons are focusing on features -- whereas what's important really is style / use-cases.
And all the articles about Scala are missing one important detail -- structural typing and type inference are not a replacement for dynamic typing and all evidence to the contrary I've seen are extremely shallow.
Truth of the matter is the choice between static and dynamic comes down to personal style and individual use-cases for which one or the other is better.
Structural typing and type inference aren't a drop in replacement for dynamic typing, but they cover 80% of the use-cases and the typically the other 20% of cases aren't that painful to encode in a static language. I'm curious which idioms from your favorite dynamic language you find the most painful to adapt to a static language.
I think that the path forward for programming will see a convergence between static and dynamic typing, but the amount of static analysis that programmers do will only increase. Certain dynamic practices such as name binding at runtime will fall out of favor, because that practice hampers static analysis. On the other hand, static type systems in use will probably become more flexible, simultaneously increasing the things that they let you automatically track while decreasing the things that the force you to track.
No they don't, it's actually the other way around -- try recreating Rails in Scala.
For that matter, give me an ORM written in Scala that's both (a) stable and consistent + (b) usable without me having to bash my head against the keyboard.
The reason why you cannot recreate a Rails clone is because Rails works basically by redefining everything at runtime, over and over again ... the whole Ruby language is built from the ground up on redefining classes (for the "class" construct, defining new classes in case they don't exist, that's just a side-effect). I'm not saying you cannot build something better in Scala (time will tell), but that something has to be a lot different and a lot more effort will be required.
Yeah, you can do "var a = 1" in Scala, that's nice.
the path forward for programming will see
a convergence between static and dynamic typing
Actually the path IMHO is a bigger separation between static and dynamic, with optional static types introduced in dynamic languages as hints for performance purposes.
Basically that's because OOP is inherently incompatible with static types and because type classes are inherently incompatible with dynamic types.
It might not cover 80% of rails, but rails isn't 100% of programming. Also, the feature lacking in Scala that would help the most with both of your examples (rails and ORM) is meta-programming, which is somewhat orthogonal to the issue of static typing. And you're right that a well designed meta-programming solution wouldn't be a clone of the Rails framework - I think it would be superior in some important ways.
Why do you say that optional static typing introduced into dynamic languages is a divergence? Coupled with looser typing from the static languages, that looks like convergence to me. And what part of OOP do you see as incompatible with typing?
Rails is just an example for which dynamic typing works great -- that's why in my initial comment I mentioned style + use-cases.
Dynamic typing simply works better when you're dealing with external-interfaces that are dynamic in nature (i.e. data-structures and interfaces defined outside of your software). Web applications, because they deal with a lot of text-manipulation and because they also deal with external data-sources and multiple layers, are the prime example for which dynamic typing works best.
Why do you say that optional static typing
introduced into dynamic languages is a divergence?
Is not a divergence per-se, but the purpose of static types is to prove correctness of types from compile-time. Dynamic languages (as they are used) only need static types for performance reasons, that's why the optimal implementation will consider these types as hints and nothing else.
what part of OOP do you see as incompatible
A static-typing system, to function properly, must be able to trace the true type of a variable throughout the code and be able to use that type at any given time in the compilation process.
In OOP you don't know the type of variables at any given time, you can only know its interface at most. And even that is incompatible with OOP, as OOP was also designed around the idea of overriding the method-dispatching being done (even in Java/Scala, all method-dispatching is done at runtime, as you're dispatching based on the implicit parameter).
For example, Scala cannot implement the Hindley–Milner algorithm for type-inference, being forever forced to the current poor implementation that only deals with inferring the type of local variables. And F# / Ocaml on the other hand really have 2 type-systems in a single language (to be able to keep Hidley-Milner , with rules for interoperability. And when working with one or the other, it's like working with different languages.
And the truth of the matter is, if you're watching closely, hybrid languages that are trying to combine static and dynamic really give you the worst of both worlds.
> (even in Java/Scala, all method-dispatching is done at runtime, as you're dispatching based on the implicit parameter).
Uh ... invokestatic?
> For example, Scala cannot implement the Hindley–Milner algorithm for type-inference, being forever forced to the current poor implementation that only deals with inferring the type of local variables.
The reason is that Scala has a much more powerful type system, than one that can be inferenced completely by HM (The HM type system was kept simple for exactly that reason).
Actually, the Scala language specification leaves out type inference for a reason: It is improved with every release to inference more and more useful things.
I'm getting slowly annoyed by those people thinking that a HM type system is "the best thing ever". Yes, it is simple, and simple is good.
But look at what Scala's type system can do as a comparison.
Please, get some idea what you are talking about before trolling about it.
"The reason why you cannot recreate a Rails clone is because Rails works basically by redefining everything at runtime, over and over again ... the whole Ruby language is built from the ground up on redefining classes (for the "class" construct, defining new classes in case they don't exist, that's just a side-effect)."
I don't think that runtime overwritting and creating of classes is
something good per se. It's just better than code generation tools in
Java. Well, monkey patching is even worse.
Why is this even needed? What is missing in the programming language
to be able to describe these things without the need of dynamic
That's why in my initial comment I talked about style.
If you haven't worked with a given style, you're prone to harsh judgments and mentalities such as this one. Redefining classes/methods/interfaces at runtime works great for a lot of people ;)
Why is this even needed?
In case you're working in Java, you may not realize it, but the libraries you're using are probably doing it.
The real difference is in these cases, say for example aspect-oriented programming or dependency injection or all the other buzzwords you had to learn for Java in the last 10 years -- which require big-ass, bloated, incomplete and extremely buggy libraries -- in Ruby/Python are as natural as "Hello World!".
To put it simpler - dynamic languages allow you to treat code as data and to mutate it in however ways you like.
Catching bugs at compile-times is not just about syntax-related errors or checking if some object responds to some method call. If you really want to catch errors at compile-time, you have to dig a whole lot deeper than that, which more evolved static languages are kind of doing (mostly by forcing you to model your data-structures in a certain way, such that the language can understand its purpose and mathematically prove stuff).
But then again, Java is not a representative of static-languages, mostly because Java is a fucked-up hybrid, having all the disadvantages of static-types with none of the advantages.
When talking about static types, you really should be talking about what Haskell can do. And even Haskell has limits that dynamic languages don't, that's why it comes with its own macro-language to alleviate that.
"works great" doesn't necessarily exclude "it's a stupid thing to do".
I wonder how things like concurrency are expected to work, if no single line of code can be sure that things it expects will exist at runtime. Or maybe exists every 5 seconds for 3 seconds. It is an optimization nightmare (proven by the abhorrent performance Ruby exhibits).
Even the Ruby "developers" have understood that monkey-patching is bad and decided to do something against it.
I agree that both static and dynamic type systems have their place, but I think this article misses the biggest con of Scala to me: the push towards stronger typing, such as that of Haskell.
I estimate fewer than 5% of programmers I've worked with truly understood type theory deeply enough to program a complex system with them properly. In addition, 0% of the systems I've worked on required such strong typing to achieve success.
Typing in Java is comparatively weak, which I view as a feature because it allows you to make more practical trades between expressiveness and simplicity.
Let's say that I have a list of objects of mixed type, and I only care that they implement a given method. With duck typing, the types of the objects need not be coupled via a common base class (or even interface). For example:
# Less coupled
for x in (A(), B()):
# More coupled
for x in (A(), B()):
This is a good article, but I must take issue with "Static typing allows you to generate the optimum bytecode/binary", simply because whilst you generally get faster binaries, you can't know that it's optimum.
Another nitpick -- static languages on top of the JVM are indeed able to generate faster binaries.
But that's only because the JVM was optimized for Java. And the fastest language around, X86 (or MIPS) assembly, is completely dynamic and loose.
I don't think it's a secret that commercial Smalltalk VMs kicked ass while executing one of the most dynamic languages in existence ... where performance was (and still is) comparable (even better in some tests) to the JVM or .NET
I don't get the claim that dynamic typing requires more unit tests. Sure if you pass a string to a Python function that expects an int it will blow up at runtime, but is there any value in testing for that?
Static types can encode a lot more information than that.
For example, suppose that you are compiling an AST. You start with an AST whose leaves are of type String (identifiers). You want to slot in something else, like pointers to a link table, or values, etc. Maybe some identifiers are builtins which will modify the AST, or user-defined macros, so the transformation will be non-trivial.
In a static language, this transformation would be typed as `AST Identifier -> AST Value`, or the like. This guarantees that you haven't missed any identifiers.
The guarantees provided by static typing tend to be somewhat orthogonal to the guarantees provided by testing. Testing ensures that, in some particular cases, the function works exactly as it should. Types ensure that, in every case, the function satisfies some properties that it should. The ideal would be every case and every property, but this is only possible with complete proofs.
In my experience, types catch more bugs with less effort than tests.
If you are doing TDD and are aiming for near complete specification and coverage, then dynamic languages and static languages will pretty much require the same number of use cases.
However, most people don't do TDD. With a dynamic language, your compiler isn't doing entire categories of checks for you. You're putting more responsibility on the tests, so you wind up writing more of them sooner and running them almost as frequently as static language users run their compilers!
Since people generally stop long before 100% coverage, you wind up with far more tests for the average dynamic project than the average static one.
Complete specification is impossible with tests, except for functions with finite domains.
Testing is about reducing bugs to a certain level. Entirely different techniques - namely formal proofs - are required to eliminate bugs. Strong static typing is more effective than tests, in terms of cost/benefit, at reducing bugs.
SendOutCards, a Web to Print company uses Scala for their complete production printing backend and the generating of Card Data. We are even currently writing a Web Dashboard using Lift which will use our Scala CardPrinting API. https://www.sendoutcards.com