Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The author doesn’t really address the issue raised in the first (longer) quoted post.

Given a set of reasonable requirements I think pretty much no one would claim a general-purpose language couldn’t satisfy them without too much trouble. In fact, that might be a fair definition of a general-purpose language.

I’m fine with strong and expressive static type-checking, but you need to hold the main limitation in mind as you use it in a distributed environment: the guarantees are static. They pertain only to your little binary and don’t say anything about the rest of the system.

People can tend to make the mistake of assuming other components in the system will continue to behave as they do on the day their own component was compiled.



I think it's absolutely addressed. With either a dynamic or static language, you have to make decisions about which fields are essential, whether you can ignore additional fields, etc. And this post shows how you can do that in Haskell.

Versioning is hard, because it's a coordination problem, but it's not the type system that produces the pain.


> And this post shows how you can do that in Haskell.

the actual critique of the first post wasn't really that Haskell can't do it in a literal sense, but that ignoring or reasoning about unknowns diminishes the advantages of static typing.

Sure, you can define an "unknown" type if you want and make your static language as permissive as possible, but then you lose the actual static guarantees, which is automating reasoning about the correctness and capacities of your program at compile time.

The question then is if you're already conceding that there are so many unknowns which better ought to be dealt with at runtime, what is there to be gained from making that explicit in your program other than just having to write more code?


> ignoring or reasoning about unknowns diminishes the advantages of static typing.

Ignoring the unknowns that are unnecessary to solve the problem at hand, is literally the essence of software abstraction. Why would you want to do more work than is necessary to solve a given problem?

> you can define an "unknown" type if you want and make your static language as permissive as possible

And 'unknown' type doesn't make the language permissive. Since the type is 'unknown' it doesn't permit any operations at at all on it. Hence, the opposite of permissive.

> you lose the actual static guarantees, which is automating reasoning about the correctness and capacities of your program at compile time.

You absolutely don't lose the actual static guarantees which are actually relevant to solving the problem your program wants to solve. If your program's job is to add a signature field to a JSON object, why would you want to parse and decode the entire JSON object into a strongly-typed value? You would just parse it into a reasonable Map data structure and add the signature as a key-value pair to the Map.

> you're already conceding that there are so many unknowns which better ought to be dealt with at runtime

That's exactly what we're not doing here, we're creating software abstractions which again, is an essential part of software development. Please see Liskov & Zilles, 'Programming with Abstract Data Types', 1974.

> what is there to be gained from making that explicit in your program

What you always gain from static typing–exposing the assumptions and invariants of your code as first-class parts of the code, making it easy for readers to understand at a glance what they are, making it easy to change the code with compiler assistance to ensure the invariants are honoured.


>What you always gain from static typing–exposing the assumptions and invariants of your code as first-class parts of the code, making it easy for readers to understand at a glance what they are

This is essentially what I was getting it. To me the static typing seems to be almost more about naming things that it is about verifying the correctness of your program in the face of unknown or interesting behaviour.

And there is a very obvious cost to this because not all code is 'your code'. That's the problem with a coupling that is mentioned in the first part of the article but not really addressed. With typing, it is very easily possible for someone to create bad abstractions that you have no control over, you simply inherit them. It breaks with the principle of leaving interpretation to the receiver of data who encapsulates it.

And this is a very practical problem in real static code, because people create bad abstractions all the time, so the type system rather than just making valuable claims about what we know produces all sort of entanglements you have no control over.

Here is where the dynamic and static typing distinction crosses over into the OO debate. Alan Kay pointed out that the 'late-binding of all things' is one of the key principles of object oriented programming. Static typing interferes with this in very strong ways by allowing developers to impose meaning on your program way too early.


> To me the static typing seems to be almost more about naming things that it is about verifying the correctness of your program in the face of unknown or interesting behaviour.

That's exactly backwards. The point is not naming things, the point is encoding behaviours and invariants in the type system so the compiler can help you check them.

> With typing, it is very easily possible for someone to create bad abstractions that you have no control over

It's possible for programs to be written badly in any typing discipline, static or dynamic. Neither saves you from the bad decisions of others. I've personally seen PHP code that no one could understand and everyone was afraid of touching because it heavily used reflection. I've heard of this for Ruby and Objective-C code too.


Wouldn't that boil down to the communication between the distributed components? I think this author's solution to that is well detailed in his post "parse, don't validate".


A general principle in a complex system of components is for each component to accept the widest range of inputs is reasonably can (and generate the narrowest range).

This improves reliability and minimizes the scope of changes needed for the system to continue to work when something changes. This is particularly important in distributed system, where you can’t generally update the system coherently and instead need/want to update it piece-meal or progressively.

Strong type systems tend to encourage you to specify strong constraints on the typed values. While you’re specifying your types you need to be careful to not over specify, making assertions you don’t need to make, ending up with a brittle system what will break unnecessarily.

There are nice advantages to strong expressive types, but the advantages are all within the local component and it takes some extra care to avoid imposing inflexibility on the system.


Her* post.


> I’m fine with strong and expressive static type-checking, but you need to hold the main limitation in mind as you use it in a distributed environment: the guarantees are static. They pertain only to your little binary and don’t say anything about the rest of the system.

To argue that a static type system is not valuable because it doesn't enforce constraints on other external systems it doesn't know about is a pretty wild case of moving the goalposts.


Some kinds of static type systems (by which I mean all of the widespread ones which aren't Haskell or similar to Haskell) make extremely specific guarantees static, such that you can't change mere implementation details without rewriting a lot of code. For example, if you're using a library which specifies floats as an input type, and you now need doubles, you're pretty well screwed: You either rewrite and recompile a huge chunk of code or you decide you don't need to expand your precision after all.

Does the library code really need to use 16-bit floating-point values, or does it only use them because someone twenty years ago believed in some superstitions about performance? That information isn't available. It isn't in the type system, so it can only be in the documentation... oh, wait, documentation focuses on How and not Why, doesn't it? Your static type system was made by people who failed to disambiguate between storage size specifications and semantics, so you're left with "16-bit IEEE floating point" as the only semantic information you're ever going to have.


Not using the polymorphism available is not a problem inherent to statically-typed programming languages.


> To argue that a static type system is not valuable

I didn’t.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: