back
135 comments
This[0] slide from Rich Hickey's Effective Programs talk[1] has really got me thinking about the benefits of costs of typing.

  "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

I haven't written as much Rust as I'd like, but one of my favorite things about the language (there's a lot of competition for "favorite" things about Rust, to be fair) is the way that types are required in function signatures, but not within functions. So if you can see the declaration of something, you can always tell what it is -- it's right there. But if it's done in a function somewhere, or if you're just trying to get a sense for the types passed around in a library, it's all on the surface of the signatures. I personally find the lack of declared types in python/javascript absolutely debilitating on large projects, so that really appeals to me.
You should check out F#! it has amazing type inference (even for functions in some simple cases, although I prefer my functions with types). The type system is still strongly typed and static. So it looks Pythonic, and behaves C#-ic.

https://fsharpforfunandprofit.com/posts/type-inference/

To be fair, there are languages where typos and inconsistency naturally fall in the middle tier unless programmers invest silly amounts of time writing and maintaining unit tests to push them down into the third tier where they belong. I can't blame Rich Hickey if he doesn't think much about those languages, but it's an important qualification for people to make when they read this slide in the context of their own work.
I feel the context is important here.

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.

After watching that talk, I would have loved somebody in the audience to point out instance like this https://raygun.com/blog/10-costly-software-errors-history/ and ask for Hickey's comments.

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.

>After watching that talk, I would have loved somebody in the audience to point out instance like this https://raygun.com/blog/10-costly-software-errors-history/ and ask for Hickey's comments. 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.

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 see nothing in those 10 costly errors that fundamentally disagree with Hickey's ranking, or advocate clearly for the need of type systems.

I also question the value of taking too much stock in outliers. How many of us will be working on the next Mars orbiter?

I strongly recommend reading about the Julia type system, which has had a lot of thought put into it:

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.

Addendum: Also really good is this video about how one should treat vectors and matrices in a type system.

Has good lessons for python, R, and Matlab. Seriously anyone who does anything with vectors should watch this.

https://www.youtube.com/watch?v=C2RO34b_oPM

There are a lot of smart, experienced, thoughtful developers who know from experience that they are more productive using types. And there are smart, experienced, thoughtful developers who know from experience that they are more productive not using types. So something interesting is going on here. Obviously this is not a situation where one of those choices is "objectively correct". It is very likely that both groups are right. So what can we learn from this? Perhaps that it depends on the developer and/or the kind of software they develop whether types make sense or not. It might also depend on how well the developer understands type systems and the kind of exposure they have had to them. I think THAT is a much more interesting conversation to have than the usual "my way is better than yours" typical conversation we have had SOOOOOOO many times.
Given enough time in a programming environment (language + libraries + tools) a smart engineer will learn or invent techniques to write correct code faster. Given enough engineers, each environment evolves a mix of techniques (unit tests, static types, correctness-by-design, defensive programming, etc) that becomes typical of that environment. The difference between Haskell and JavaScript might be the usual cliché, but there is an entire spectrum and even within a single language the mix of techniques might change depending on the application (financial software, video games, web sites).

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).

The only type we need is char pointer. We can then hard code the offsets in a macro to reference members of a strict.
I'm a huge strong static typing fan. I find that I get in both situations though. For a quick script in Python, types might be a hindrance. If I write something more complex, strong types can be used to build a lot of the logic into the types, avoiding costly errors and raising maintainability in the long term.
I have yet to see reliable commentary from developers who used a good static type system for an extended period of time and still willingly prefer dynamic types. For instance, Rich Hickey makes fun of how long it takes to learn Haskell. I'm not aware of him having done significant work in anything better than Java.
I think Rich Hickey has significant experience with Haskell and there's at least one former Haskell user on HN who has transitioned to Clojure. I still don't quite get their point but I don't think they're speaking from ignorance.
Didn't go through it all yet, but its premise seems kind of off: you can't really discuss "types in isolation of the language"!

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...

> In Python I can get a lot done and really don't miss types at all

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...

Read the article again. He says Python is unityped. The word "type" in mathematics and computer science has a meaning which is not the same as your usage of the word.

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.

Python has optional static typing since at least 3.6[1]. apistar[2], a REST framework, uses modern Python's typing to serialize data, which is neat.

[1] https://docs.python.org/3/whatsnew/3.6.html#pep-526-syntax-f...

[2] https://github.com/encode/apistar

Robert Harper is saying the exact opposite of what you’re quoting him to say. He would 100% call Python unityped (at least prior to the new type annotations, and probably even with them).
> TDD…) are a direct consequence of that lack of static checking.

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.

The things in Python you call types are traditionally referred to as tags. Tagged values are values of one type (the type in Python) but which can still be distinguished based on a run-time check of their tag.
I don't know, I have not written a ton of Python, but after spending a significant amount of time with Rust and Swift, which give you adequate tools to provably eliminate an overwhelming majority of runtime exceptions, I never want to go back.
I feel the same with ReasonML / OCaml. I've spent hours on a refactor and everything work beautifully after fixing all the type errors.
> 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...

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.

When you're having trouble justifying at compile time that a property holds, it's not always because you don't know why it holds, but frequently a problem with the type system you have to write the justification in. Some type systems are so limited that you can't even express the conditions under which the property holds, let alone prove them. Others are powerful enough to prove anything, but that makes type inference intractable, because the inferencer might have to prove arbitrary theorems.

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.

[1] https://arxiv.org/abs/1502.01278

> When you have proper type inference [...] what advantages do dynamic languages really have?

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...

One benefit is a more robust execution model, in which objects know what type they are. Given a machine word and a RAM dump, I can tell you the type of the object in that word or the location that the word points to. I don't need to know the address of the piece of machine code which is manipulating that word, which, in turn, I don't need to correlate to the matching source code via debug info.

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.

>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!

F# does this amazingly well. Rust and Scala to a certain degree.

String versus list of string continues to be a rough edge in Python, both being iterable.
Yep, that one caught me a few times.
> both being iterable

Which is a great thing.

My experience is the same with Common Lisp: it is very strongly typed, so type mismatches never pass without an explicit error.
Skimmed the video, and it's interesting.

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)?

For the most part I feel that for 99% of problems types just “get in my way”.

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.

I think what you're talking about here is static types getting in your way, not just types getting in your way.

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.

I used to feel this way, but the benefits of a good type system far outweigh the drawbacks IMO. A lot of runtime errors caused by dynamic types get pushed back to compile time, which is a much safer and easier time to deal with them. Nowadays writing code without type checking feels like building a house on quicksand.

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.

The types will make reading the code multiple times easier when someone else reads the code. Or when you go back to the code six months later.
Don't take this the wrong way, but I suspect you are young developer who hasn't realised that putting in the extra work up front pays large dividends down the line.
As pointed out in the video, types can be helpful for bigger projects, where others have to read your code, and sometimes for refactoring.
Javascript and Python are very popular dynamic languages, this doesn't make them good dynamic languages.

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.

I think it's quite reasonable to think that many developers don't have an intuition for types and thus might not care about them. Also given the title I think this is meant to be an introductory discussion and the premise is then valid.
OP link should be replaced with this one.
The video is an okay discussion of type systems and the pro's and cons, but for me it didn't bring anything new to the table.
I think a lot of problems with types could be avoided if the package management system / linking system required typing of imported functions, even if the actual language doesn't.

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.

FKING VOLUME WARNING !!!

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.