top | item 45629639

(no title)

weavejester | 4 months ago

Static typing is a useful constraint, but it's not the only constraint. Focusing too much on dynamic vs. static typing can make one miss the more general problem: we want code that's expressive enough to do what we want, while being constrained enough to not do what we don't.

Immutability, for example, is another great constraint that's not considered in the article, but should certainly be on your mind if you're deciding between, say, Rust and Java.

The article delves into some of the drawbacks of static typing, in that while it can be more expressive, it can also contain a lot of information that's useful for the compiler but decidedly less useful for a reader. The Rust example that loads a SQL resultset into a collection of structs is a standard problem with dealing with data that's coming from outside of your static type system.

The author's solution to this is the classic one: we just need a Sufficiently Smart Compilerâ„¢. Now, don't me wrong; compilers have gotten a lot better, and Rust is the poster child of what a good compiler can accomplish. But it feels optimistic to believe that a future compiler will entirely solve the current drawbacks of static typing.

I was also slightly surprised when templates were suggested. Surely if you're aiming for rigor and correctness, you want to be dealing with properly typed data structures.

discuss

order

addaon|4 months ago

> we want code that's expressive enough to do what we want, while being constrained enough to not do what we don't

I don't think that's an ideal mental model. Code in any (useful) language can do what you want, and can not do what you don't want. The question is how far that code is from code that breaks those properties -- using a distance measure that takes into account likelihood of a given defect being written by a coder, passing code review, being missed in testing, etc. (Which is a key point -- the distance metric changes with your quality processes! The ideal language for a person writing on their own with maybe some unit testing is not the same as for a team with rigorous quality processes.) Static typing is not about making correct code better, it's about making incorrect code more likely to be detected earlier in the process (by you, not your customers).

weavejester|4 months ago

I was being glib, so let me expand on what I said a little.

By 'constraint' I mean something the language disallows or at least discourages. Constraints in software development are generally intended to eliminate certain classes of errors. Static typing, immutability, variable scoping, automatic memory management and encapsulation are all examples of constraints, and represent control that the language takes away from the developer (or at least hides behind 'unsafe' APIs).

By 'expressiveness' I mean a rough measurement of how concisely a language can implement functionality. I'm not talking code golf here; I mean more the size of the AST than the actual number of bytes in the source files.

Adding constraints to a language does not necessarily reduce its overall expressiveness, but static typing is one of those constraints that typically does have a negative effect on language expressiveness. Some will argue that static typing is worth it regardless, or that this isn't an inherent problem with static typing, but one that stems from inadequate compilers.

hakunin|4 months ago

Everything has a cost. If you had to pick between "write 99% correct code in 1 week" vs "write 100% correct code in 1 year", you probably would pick the former, and just solve the 1% as you go. It's an absurd hypothetical, but illustrates that it's not just about correctness. Cost matters.

What often annoys me about proponents of static typing is that they sound like it doesn't have a cost. But it does.

1. It makes syntax more verbose, harder to see the "story" among the "metadata".

2. It makes code less composable, meaning that everything requires complex interfaces to support everything else.

3. It encourages reuse of fewer general types across the codebase, vs narrow scoped situational ones.

4. It optimizes for "everything must be protected from everything" mentality, when in reality you only have like 2-5 possible data entries into your system.

5. It makes tests more complex to write.

6. Compiled languages are less likely to give you a powerful/practical REPL in a live environment.

For some, this loses more than it gains.

Also, albeit I haven't seen this studied, human factor probably plays bigger role here than we realize. Too many road signs ironically make roads less safe due to distraction. When my code looks simple and small, my brain gets to focus better on "what can go wrong specifically here". When the language demands I spend my attention constructing types, and add more and more noise, it leaves me less energy and perspective on just taking a step back and thinking "what's actually happening here".

jweir|4 months ago

Something that the type system should do is "make impossible states impossible" as Evan Czaplicki said (maybe others too)

We have started to use typed HTML templates in Ruby using Sorbet. It definitely prevents some production bugs (our old HAML templates would have `nil` errors when first going into production).

alexpetros|4 months ago

I have typically understood the "Sufficiently Smart Compiler" to be one that can arrive at the platonic performance ideal of some procedure, regardless of how the steps in that procedure are actually expressed (as long as they are technically correct). This is probably impossible.

What I'm proposing is quite a bit more reasonable—so reasonable that versions of it exist in various ecosystems. I just think they can be better and am essentially thinking out loud about how I'd like that to work.

weavejester|4 months ago

I'm fully on board with improving compilers. My issue is that you compare the current state of (some) dynamically-typed languages with a hypothetical future state of statically-typed languages.

You use `req.cookies['token']` as an example of a subtle bug in JavaScript, but this isn't necessarily an inherent bug to dynamic typing in general. You could, for example, have a key lookup function that requires you to pass in a default value, or callback to handle what occurs if the value is missing.

    req.cookies.get('token', () => {
      throw new AuthFailure("Missing token")
    })

andersmurphy|4 months ago

I agree with this. I value immutability much more than static types. I find it eliminates a much larger class of bugs without sacrificing expressiveness.

vlovich123|4 months ago

> we want code that's expressive enough to do what we want, while being constrained enough to not do what we don't.

Have you somehow solved the halting problem? AFAIK, all Turing complete languages are perfectly capable of expressing the exact same programs.

tialaramex|4 months ago

The price for not making a Turing Complete language is that you can't solve all possible problems. But, you probably didn't want to solve all possible problems.

That's one of the insights in WUFFS. Yes, most problems cannot be solved with WUFFS, but, we often don't want to solve those problems so that's fine. WUFFS code, even written by an incompetent noob, categorically does not have most of the notorious problems from systems languages, yet in the hands of an expert it's as fast or faster. It has a very limited purpose, but... why aren't we making more of these special purpose languages with their excellent safety and performance, rather than building so many Swiss Army Chainsaw languages which are more dangerous but slower ?

otikik|4 months ago

A program does not tell the machine what to do. It tells the next programmer what we wanted the machine to do.

weavejester|4 months ago

There's a difference 'theoretically possible' and 'practically feasible'. Assume I'm talking about the latter.