3 ms·
This is a straw man argument. Sure, if your types are simple like int, string, or float, you might as well write them down rather than keep them in your head.
by jonnytran 7y ago
This is a straw man argument. Sure, if your types are simple like int, string, or float, you might as well write them down rather than keep them in your head. They double as documentation. However, the other cases are where it matters.
1. When the types are complicated enough, people no longer read them. They skim over them and don't bother to ever "load" them into short term memory. Sometimes it makes sense to make a type alias for the subcomponents, but many times they're one-off types and it wouldn't make sense. As the person writing the code, figuring out what the types actually are and writing them down doesn't actually help anyone. It's just busy work that the compiler or interpreter could have inferred. For example, if I have a hash nested in an array, nested in a set, you can access them all with brackets chained together. I don't actually need to know the full type in order to use it. So why bother figuring out the exact type that it is? Which brings us to...
2. When the types are abstract, with type variables and type constraints, someone has to put in the work to come up with the most sensible type signature. Sometimes that is the most general type that the code satisfies (using a Rust trait, Haskell typeclass, or a Java interface), but sometimes it's not. It really depends on the context. Should I abstract over this type and introduce a type parameter to the function? Or should the type parameter be for the entire struct/class? These are oftentimes complicated questions, with no clear answer. Weighing your options and choosing is real work. If I now say that this function will accept different sized ints, I may no longer be able to just call i64::something(). I literally need to change how I write my code or even import a new helper library to do it. (I have had to do that in Rust. And I love Rust, BTW.)
OTOH, in a dynamic language, all of that work is just gone.
What I'm saying is that types for a compiler must be precise and general. But types that you keep in short term memory are oftentimes vague (something that I can index, or some kind of number) or specific to a test case. And it doesn't matter.
- zapzupnz 7y ago> This is a straw man argument. That would imply ill intent on my part, and there was nothing of the sort. All I said was that I couldn't see the sense in certain things and, without further explanation, I didn't. Other comments helpfully pointed out the context for why the assertion is so often made and I was happy with it. On the other hand, I think I'd need to see some hard evidence for the following assertions: > When the types are complicated enough, people no longer read them What is a complicated type? > As the person writing the code, figuring out what the types actually are and writing them down doesn't actually help anyone. Citation needed. I find static typing tremendously helpful in my own code, especially if it's code I haven't worked on in a long while. > It's just busy work that the compiler or interpreter could have inferred … such that I only notice errors when the code is running, not beforehand. Without type annotations, interpreted languages like Python and Ruby are happy to let wrong types in function calls and message passes slide — which is why frameworks like Sorbet exist, to help eliminate that class of error. > 2. All of that is language-specific, surely, not really anything to do with static or dynamic typing. That whole mechanism is quite straight-forward and has a well-prescribed mechanism in Swift, for instance, with protocols, extensions, and conditional conformance. > OTOH, in a dynamic language, all of that work is just gone. I mean, it's not just gone. There are trade-offs, of course there are; but the assertion that the work is gone also implies the potential associated errors are gone seems a bit ingenuous to my eyes. Yeah, types can be a bit constraining — but at the same time, if you reason about them properly and according to the language's prescribed mechanism rather than fight them, it can and does become second nature. At that point, using types becomes as simple for some as not using types is for others.
- jonnytran 7y agoA better example of a complicated type that no one ever loads into their short term memory is the type of a parsed JSON object from an API response. No one ever bothers to figure out or reason about the type. They have documentation or an example response that they just navigate. If you want to advocate something like protobufs, that's a separate issue from static vs. dynamic typing. The point is that in a dynamic language you literally _don't have to_ reason about many types at all. Or you do it in a completely different way that doesn't involve holding the type in your short term memory.
- philwelch 7y agoIn most statically typed languages you end up with a class/struct type that encapsulates the API response you are either generating or parsing. When you don’t have this, you have to either assume or defensively program around the presence or absence of certain fields in certain contexts. “Everything is a hash/dict/JSobject” doesn’t absolve you of having to reason about which fields an API payload contains, it just forces you to do it all by hand rather than simply defining a data type and telling your serializer/deserializer “this is what I want” (and then operating on a strongly typed object rather than an arbitrary hash table).