It's not the types that fail, but the type system and its misuse.
> This is like pretending really hard
No, that's not pretending — static typing is literally about theorem proving, type theory being equivalent with math logic. This is the famous Curry-Howard isomorphism, types corresponding to propositions.
Of course, you can have situations in which your type system cannot describe the propositions that you need, or situations in which holes are left for pragmatism. Such holes include for example the ability to use Object and then downcast in Java and have the implicit contract that the people using them know what they are doing, being at the same time a symptom of the type system not being expressive enough.
In TypeScript in particular you can just use the "any" type, which is there for pragmatism and because JavaScript developers would have had a hard time adopting it otherwise. It doesn't help that TypeScript's generics are unsound either.
Of course, you can point at "the outside world" and say that you can't trust it to deliver the types that you expect. But with a good type system, you only need to do that runtime validation only once, at the edge where you receive the data and afterwards the statically typed compiler can take over that responsibility. And indeed, in my experience libraries for JSON parsing in Scala (e.g. Circe) or Haskell (e.g. Aeson) are very well behaved and due to automatic deriving, they make that validation painless in 90% of cases, because the runtime validation is derived from the static types automatically, no work required, something I haven't seen in TypeScript.
The whole notion of "optional types" is my opinion flawed.
You either work with static typing, or with dynamic typing and choosing a side will change the way you work, it will change your mentality, your approach to problem solving.
Working with optional types, which is what people tend to do in languages like TypeScript, brings you the very worst of both worlds.
Optional static typing doesn't work. A contracts system, like Clojure's Spec, works much better for dynamic typing and yes, there are fundamental differences, basically the difference between "for all" and "exists", or between compile time and runtime.
---
> Convention: Pretend immutability
N.B. in an actual FP language, immutability is not a convention, but something being enforced by how data structures are built (e.g. persistent data structures), via the type system or the runtime system — even in Java, you'll have a really hard time to change the definition of a class or of a "final", or to mutate a persistent collection (see vavr.io).
---
I like TypeScript overall, I think it's an improvement over JavaScript and it certainly has pretty cool features — but don't use it to judge either static typing or dynamic typing or functional programming for that matter.
If you want to see actual dynamic typing and how it can shine, use ClojureScript. If you want to see actual static typing and how it can kick ass, use PureScript.
And both are good for giving you a real taste of functional programming. JavaScript and JavaScript++ languages, are really poor at FP, mostly due to the ecosystem's culture, and the very few FP libraries that exist for JavaScript are very unpopular, with only a few gems here and there, like Rx.JS. So if you're doing JavaScript or TypeScript, chances are your codebase doesn't do much FP.
Did you read the whole thing? This is exactly the point the author is trying to make. Look, you both arrive at the same conclusion even:
> Or you change the game and just use PureScript. Or ReasonML, or Elm, or even ClojureScript.
> If you want to see actual dynamic typing and how it can shine, use ClojureScript. If you want to see actual static typing and how it can kick ass, use PureScript.
But the conclusion doesn't jive with the comments he was responding to. I found the article a bit annoying because of that, though otherwise interesting.
> Such holes include for example the ability to use Object and then downcast in Java and have the implicit contract that the people using them know what they are doing,
It's worth noting that this is still not quite the same as the leeway that most dynamic languages allow you, because, while you can downcast, all such downcasts are checked. You can't just say that Foo is a Bar, and proceed to treat it as a Bar, in Java and similar languages. You can assert (via a typecast) that it ought to be a Bar - but if it's not, you just get an exception or some other indication of failure.
As a result, it's much, much more difficult to hammer square pegs into round holes in those languages. As it should be.
I instead proclaim that it brings you the best of both worlds. Finding the right mix of static/dymamic is a balance act but if you do so you get the benefits of static types (safety, documentation, tooling) and dynamic types (fast prototyping, not having to write convuluted code to please the compiler). What are the killer features that gradual types lose out on?
Sure,and lots of codebases are completely untyped. I find it great that you can opt out of the theorem proving that type checking is when crunch time comes. As such it functions as a loan and your organization should strive to pay back the debt when it can. Nothing can save you if you never get a calmer period.
I think it’s the worst of both worlds, you still have to deal with types (often manually even), but you can’t trust your types since they’re not pervasive.
The killer feature is global type inference. I pretty much prototype in Elm as fast (or faster) than any dynamic language, and when the code compiles it’s free of runtime error except at the boundaries (i.e JS interop). This allows me to focus on fixing error arising from my misundetstanding of the problem (and my general incompentence).
It's not the types that fail, but the type system and its misuse.
> This is like pretending really hard
No, that's not pretending — static typing is literally about theorem proving, type theory being equivalent with math logic. This is the famous Curry-Howard isomorphism, types corresponding to propositions.
Of course, you can have situations in which your type system cannot describe the propositions that you need, or situations in which holes are left for pragmatism. Such holes include for example the ability to use Object and then downcast in Java and have the implicit contract that the people using them know what they are doing, being at the same time a symptom of the type system not being expressive enough.
In TypeScript in particular you can just use the "any" type, which is there for pragmatism and because JavaScript developers would have had a hard time adopting it otherwise. It doesn't help that TypeScript's generics are unsound either.
Of course, you can point at "the outside world" and say that you can't trust it to deliver the types that you expect. But with a good type system, you only need to do that runtime validation only once, at the edge where you receive the data and afterwards the statically typed compiler can take over that responsibility. And indeed, in my experience libraries for JSON parsing in Scala (e.g. Circe) or Haskell (e.g. Aeson) are very well behaved and due to automatic deriving, they make that validation painless in 90% of cases, because the runtime validation is derived from the static types automatically, no work required, something I haven't seen in TypeScript.
The whole notion of "optional types" is my opinion flawed.
You either work with static typing, or with dynamic typing and choosing a side will change the way you work, it will change your mentality, your approach to problem solving.
Working with optional types, which is what people tend to do in languages like TypeScript, brings you the very worst of both worlds.
Optional static typing doesn't work. A contracts system, like Clojure's Spec, works much better for dynamic typing and yes, there are fundamental differences, basically the difference between "for all" and "exists", or between compile time and runtime.
---
> Convention: Pretend immutability
N.B. in an actual FP language, immutability is not a convention, but something being enforced by how data structures are built (e.g. persistent data structures), via the type system or the runtime system — even in Java, you'll have a really hard time to change the definition of a class or of a "final", or to mutate a persistent collection (see vavr.io).
---
I like TypeScript overall, I think it's an improvement over JavaScript and it certainly has pretty cool features — but don't use it to judge either static typing or dynamic typing or functional programming for that matter.
If you want to see actual dynamic typing and how it can shine, use ClojureScript. If you want to see actual static typing and how it can kick ass, use PureScript.
And both are good for giving you a real taste of functional programming. JavaScript and JavaScript++ languages, are really poor at FP, mostly due to the ecosystem's culture, and the very few FP libraries that exist for JavaScript are very unpopular, with only a few gems here and there, like Rx.JS. So if you're doing JavaScript or TypeScript, chances are your codebase doesn't do much FP.