"The Problems of Programming"
Domain Complexity
Misconception
10x
Place Oriented Programming
Weak support for information
Brittleness/Coupling
Language model complexity
Parochialism/context
weak support for Names
Distribution
Resource utilization
Runtime intangibility
Libraries
Concurrency
10x
Inconsistency
Typos
There are some type systems, for example Rust's, which integrate RAII into the language as a first class concept and are able to address some of the upper level concerns like concurrency and place oriented programming. In general, however, there are many problems on this list where more complex type systems doesn't help, or even makes it worse.[0] https://twitter.com/stuarthalloway/status/926065084652228609 [1] https://www.youtube.com/watch?v=2V1FtfBDsLU
This slide is not a comment on the frequency of these problems, because typos are definitely one of most if not the most frequently occurring issue in the day-to-day work of most developers, and I'm sure Rich Hickey realizes that.
Rather, this slide is a comment on what he defines as the "severity" of these problems, and one of the important manifestations of severity is the cost of getting these wrong.
With the exception of certain business domains where getting things perfectly right the first time is paramount, such as financial transactions and safety/security-critical programs, the cost of typos and inconsistencies is generally minuscule because they're practically costless to debug and fix. In fact, they often cost so little that we end up instinctively fixing typos and inconsistencies countless times throughout our day to day development processes, often without ever even making a conscious effort to identify and fix them. Even if the occasional typo makes it past our tests and into production, these issues are almost always trivial to debug and fix (and if they're not, and you don't happen to be in of these domains where getting things right the very first time is paramount, then oftentimes that can be the smell of some deficiency in your deployment/monitoring setup, or a manifestation of some other more subtle, but more severe problems listed on the slide, such as brittleness/coupling, place oriented programming, weak support for concurrency/names, etc).
Of course, being able to outright eliminate these entire classes of errors is a legitimate benefit of a static type system, and Rich Hickey acknowledges this benefit later in the talk. If you're in one of those business domains where you absolutely cannot afford to let typos and inconsistencies sneak their way into your production systems because the they'd be disproportionately costly to fix, or result in consequences that you're not willing to accept, then static typing can be very useful for providing those safeguards.
However, I think he also brings up an important point that static type systems and the act of flowing types around in your system is often a significant source of coupling, and that coupling is a much more severe problem when it comes to maintaining a piece of software over the long term. So it's important for each team to assess that tradeoff carefully in order to gauge if they're willing to take on that additional level of coupling in exchange for eliminating the possibility of typos and inconsistencies from reaching production.
Needless to say I really don't agree with that list, as history shows the inconsistency, typos, lack basic units and the ability to express them in a computation etc., have been more catastrophic than he believes them to be. And these issues cannot really be fixed without a proper type system, that will not allow stuff like that to get into production.
And a good type system cannot be optional, e.g. clojure's spec, There are number of studies that show that will power, is not enough, there needs to be and environment - a type system - that helps to avoid and catch mistakes.
From the list you cite, only three of 10 errors are caused by "typos", "lack of basic units" and type errors.
And this is just a particular list.
Recalling Rich Hickey list, the things that cause more problem in code, from worst to less damaging:
Domain Complexity
Misconception
10x
Place Oriented Programming
Weak support for information
Brittleness/Coupling
Language model complexity
Parochialism/context
weak support for Names
Distribution
Resource utilization
Runtime intangibility
Libraries
Concurrency
10x
Inconsistency
Typos
I'm pretty sure that if we compile a list of real-life cases where the major reasons of failure according to Rich Hickey have caused trouble, the list of examples would be immense, compared to "typos".I also question the value of taking too much stock in outliers. How many of us will be working on the next Mars orbiter?
https://docs.julialang.org/en/stable/manual/types/
Although it's "untyped" (in the sense of the video), because it's strongly typed, there are plenty of nonbrittle optimizations that are performed by the compiler on account of the type system, and the result is something that's often within 1~1.5x speed of C.
At the same time, I had to write my own numerical system and because of Julia's type system i could immediately plug it into the standard library's matrix algebra and fast fourier transform algorithms and do comparative analyses out of the box. You can even do exotic things -- I was testing some reed-solomon encoding and created a galois field type, and the standard library matrix solve algorithm worked out of the box on my custom type.
Has good lessons for python, R, and Matlab. Seriously anyone who does anything with vectors should watch this.
Moving to a different environment where those techniques are no longer as effective will result in lower productivity for a while. Moving to an environment where those techniques can no longer be used (no type-checking, no ability to unit test) results in frustration, bargaining and hopefully acceptance, but the end result is that everyone stays on their side of the fence depending on whether they identify as "relies on type-checking to ensure correctness".
By the way, funny how sometimes, we get something out of bargaining (TypeScript because there is no type-checking in JavaScript, Selenium because there is no unit-testing in graphical user interfaces).
Just a basic example of two dynamic languages: In Python I can get a lot done and really don't miss types at all. In JS otoh, I feel lost until I pull in TypeScript to tame it, the language makes it even hard to think clearly without types, like, "is that an object or a dict" or "is it a map? from what to what?" etc. For most usual programmer mistakes that would be type errors in typed language, Python still throws a meaningful and debuggable runtime error, whereas JS carries on an breaks much further from the cause of the problems, leaving you to debug WTF after WTF...
And static typing is great, as long as it doesn't require you to type variables and anonymous or small utility functions/methods... eg. it sucks without inference!
Some languages work better with types, some don't gain much from them...
No wonder, because Python has types (well … most everything does; but Python isn’t unityped [1]). It’s even relatively strongly typed. It’s just not statically typed (what the video calls “typed”).
That said, I do miss static type checking in Python, and a lot of the tooling and methodology around Python (pylint, TDD…) are a direct consequence of that lack of static checking.
— You seem to be aware of this but the distinction between “has types” and “has static typing” is actually fairly important. APIs in general often take advantage of Python’s extensible type system.
[1] See e.g. https://existentialtype.wordpress.com/2011/03/19/dynamic-lan...
Well-typed programs should not go wrong. Python programs can crash at runtime due to simple type errors (expected integer but got a string). A crash is a crash, from a theoretical point of view it matters not that the error is prettier than a segmentation fault. Python is not strongly typed, it just has good error messages.
[1] https://docs.python.org/3/whatsnew/3.6.html#pep-526-syntax-f...
Static checking gives you basic unit testing. TDD is far more expansive than unit testing. Static checking is not going to integration test your app for you.
> Some languages work better with types, some don't gain much from them...
When you have proper type inference (which has been around for literally decades), what advantages do dynamic languages really have? I don't see any significant benefits personally. If you you're having trouble justifying at compile type a property holds, more than likely you've got a bug that's going to bite you later.
The advantage of dynamically typed languages is that you are not glued to one specific proof language and don't have to provide proofs for trivial properties, or one-off programs you only need to run once. There are some type systems that attempt to provide similar benefits, e.g. success typing puts the burden of proof on the compiler which will only reject a program if it can prove that the program is incorrect [1]. But as far as I know, that approach has not been included in any mainstream programming languages.
You can quickly prototype by repl-ing around half-working pieces of code until you get to something that you can conceptualize. Sometimes you start from "how to" knowledge, but you don't really have the "what is" knowledge. You "know how to make it work" but you don't yet have any idea of "what it actually does". You'll get to that stage, but you need a prototype you can play with in the meantime.
Mathematicians have the hardest time groking this, so I give them the example of drawing an ellipse: let's say you start with a "working definition" of "an ellipse is that ovalishy thinggy that I get by dragging a pencil on a loop of string tied to two nails set at some distance". You know how to produce it, you want to play with it, but have no idea how to define it yet.
A dynamic language gives you the higher level equivalent of being able to "draw the damn ellipse even if you have no idea what it is"... later you can look at the code for drawing the ellipse and extract from it that "aha, it is what you get by constraining the sum of distances from 2 points to be constant!".
But some people (like me), are not very good at abstract thinking, so we need to play with half-formed stuff on screen, like "let's do this api call" and "do that and that to the returned data" and "pass it to the other thing" that "maybe someone forgot to document" and "see what it does"... and reverse engineer the abstractions from working code and maybe later properly generalize them.
I've never seen a static language with a good interactive prototyping experience so far. Though I'm playing with OCaml/Reason now, and its REPL seems quite powerful, though the language is more verbose/pedantic than I'd like it...
Type inference is compatible with dynamism.
Infer all you want. Optimize and diagnose, just don't strip type from my objects, please, and don't say I can't try running something because it wasn't completely checked.
F# does this amazingly well. Rust and Scala to a certain degree.
Which is a great thing.
I find the (implicit) premise that you might not already care about types hard to believe... Migrating a JavaScript project of any complexity to typescript almost always reveals errors "for free" because of the typing. Certainly any experience hacking on python would also teach you the lesson of declaring your types ahead of time.
Is this old-fashioned thinking? Are types the SQL of yesteryear (in the context of the crazy rush to "web scale" mongodb)?
It feels like I already know what is going to go in where, and having to type it is just a waste of time.
That said, when I have to work with APIs that can not guarantee their data integrity throwing in a quick @flow annotation of top of the file, and taking the time to write out what is optional has proven very valuable to make sure my functions, and subsequent code is not going to throw.
Another reason I like flow more is because I don’t need to convonce my team to use it, I can just use it myself, and then delete it after I am done writing my code.
Strong/weak types and dynamic/static types are really two different spectrums. People conflate strong types with static types and weak types with dynamic types, but they aren't really the same thing.
Static/dynamic just has to do with whether the types are checked at compile time or run time. Examples: Static: C, Haskell. Dynamic: Javascript, Python.
Weak/strong has to do with what kinds of checks the type system does. A strong type system is capable of checking a lot of different things for you. Static types are often stronger, but not always: for example, C is statically typed but its type system checks hardly anything: int* + int is perfectly valid. A list of programming languages from weakest-typed to strongest-typed might look something like: JavaScript, C, C++, Common Lisp, Perl, Scheme, Ruby, Python, Java, C#, OCaml, Haskell.
Static types are nice for projects which will grow large and where bugs are a big problem, but I think for the average HN person, static types aren't really necessary.
Strong types, on the other hand, are extremely useful. Even in a dynamically typed language, they aid in debugging a lot, because type errors occur much closer to where they're caused. In Python, for example, `"foo" + 42` immediately fails. But in JavaScript, you don't get an error until much later, perhaps when your webpage is mysteriously displaying "foo42".
Of course, there's a small cost to strong types: in the case where I actually do want to append a number to a string, I have to do `"foo" + str(42)`. But I think people tend to overstate this cost because it's visible. But if you look at the big picture, typing five extra characters takes a lot less time than debugging almost anything.
There's also the self-documenting aspect of strongly typed languages: for instance, if I look up the documentation on a javascript API, I have to hope function parameters have been specified well, otherwise I just have to guess what should be passed in or dig through the source. With a strongly typed language I probably get that information as part of the autocomplete hint.
And good type systems can be powerful tools. In Swift for example, the protocol system is powerful enough that I'm sure it results in writing less code overall, not more.
In particular OOP is very much helped by static types.
Clojure would a better example of a dynamic language that would not be improved by adding static typing. Namespaces, functions and immutable data (as well as pervasive use of data instead of wrapping it in classes) lessens the downsides I bump into continuously when doing javascript development.
Maybe there'll be a "safe" subset of the pip/npm registry that will only let typed APIs in, to promote the concept.
As a side-note: is there data on how many dependencies there are of framework on npm, on average? If it's anywhere past two, I'd say that the extra time spent typing is more than compensated by the time saved by the user of the API.
This video will blow your fking eardrums out because at the 4:37 minute mark, the presenter switches on his microphone system and the input source of the video changes to be about 400% what it was previously.
The fact that nobody has mentioned this is a pretty strong indication that none of these people have actually watched this video.