Using compile time checked typed languages is the kind of continuous saving/benefit that you really feel when it's not there. For me:
- type signatures offer succinct descriptions of what a function does
- I write less unit tests with proper usage of types (if you need a natural number, take a natural number type, not an integer)
- building your domain and giving names to structures that are in the application (passing around MailerOptions as opposed to an 'object that contains ...')
- concrete interfaces for components/services/modules in my codebase
> it's much easier to enforce coding standards, especially if you follow XP and pair all the time.
IMO where it's possible to add process that makes the bad thing an error, I do that instead of requiring/trying to enforce discipline.
> or example, in large dynamic codebases I've worked on, calling something `object` when it really means `object_id` would never fly. Variables and functions always have to be full words describing exactly what they do. Functions are only ever allowed to return one type. And of course everything is thoroughly tested.
Agree on all fo this, but regarding tests, using types does prevent me from having to worry too much.
There was a point in time where writing lots of unit tests that tested non-user inputs (is the incoming thing a number? is it less than zero somehow) was seen as clever (and it still is a good idea where appropriate). These classes of bugs just don't exist in a properly typed codebase. If you need an Integer that can't be less than zero, you want a Natural (haskell parlance).
Excessive unit tests in untyped codebases are the result of not properly specified types. If you have a function that takes a certain type, there is no reason to check if you got a string (or not that type). If you pick a language with a top of the line type system, you don't even have to check for nil.
Realizing that nothing is actually safe in languages with nullable types was a big jarring point for me way back when. I think it was when Java introduced Optional<T> that I realized it should actually be everywhere, and all the Haskell I'd been doing on the side just made way more sense.
> I'm writing this just to give the perspective of a dynamic weenie and maybe looking to be convinced a little more? I also know three incredibly good programmers with 20-50 years experience each who all wrote in typed languages for years and then couldn't be happier not to have types when they moved to Ruby. So ya, I dunno what I'm expecting here, but mostly offering my perspective.
So I say the "convincing" part a bit lightly -- I know that people must come to their own conclusions, I can only suggest the idea. People in both typed and untyped languages can be incredibly productive (my friend is much like you, so productive and disciplined that types don't seem to add much), and I can admit that adding types requires more up front thought (good) and more ceremony (~bad, especially if you don't have type inference).
Maybe the big problem is that the really good implementation of types is in languages that can be hard to approach (Haskell, OCaml, other ML langauges). The second best IMO is Rust but that has the whole separate ramp of ownership/borrowing which is hard for different reasons. Go has some good concepts (protocols) but a bunch of other warts.
I find that writing software with a good type system gives me confidence that the code is correct. It's almost axiomatic because you're building the structures that are being checked/used by the code, but nevertheless I have that much more confidence in a Typescript codebase or a Haskell codebase than an equivalent untyped codebase.
Basically I think like this:
- If I want correct, I write Haskell
- If I want quick, I write Typescript
- If I want performance, correctness and efficiency, I write Rust
Also, even if you don't believe me at all, there is a trend -- python adding typing, sorbet and other systems for ruby, typescript gaining popularity, etc. The crowd sometimes has some wisdom.
- type signatures offer succinct descriptions of what a function does - I write less unit tests with proper usage of types (if you need a natural number, take a natural number type, not an integer) - building your domain and giving names to structures that are in the application (passing around MailerOptions as opposed to an 'object that contains ...') - concrete interfaces for components/services/modules in my codebase
> it's much easier to enforce coding standards, especially if you follow XP and pair all the time.
IMO where it's possible to add process that makes the bad thing an error, I do that instead of requiring/trying to enforce discipline.
> or example, in large dynamic codebases I've worked on, calling something `object` when it really means `object_id` would never fly. Variables and functions always have to be full words describing exactly what they do. Functions are only ever allowed to return one type. And of course everything is thoroughly tested.
Agree on all fo this, but regarding tests, using types does prevent me from having to worry too much.
There was a point in time where writing lots of unit tests that tested non-user inputs (is the incoming thing a number? is it less than zero somehow) was seen as clever (and it still is a good idea where appropriate). These classes of bugs just don't exist in a properly typed codebase. If you need an Integer that can't be less than zero, you want a Natural (haskell parlance).
Excessive unit tests in untyped codebases are the result of not properly specified types. If you have a function that takes a certain type, there is no reason to check if you got a string (or not that type). If you pick a language with a top of the line type system, you don't even have to check for nil.
Realizing that nothing is actually safe in languages with nullable types was a big jarring point for me way back when. I think it was when Java introduced Optional<T> that I realized it should actually be everywhere, and all the Haskell I'd been doing on the side just made way more sense.
> I'm writing this just to give the perspective of a dynamic weenie and maybe looking to be convinced a little more? I also know three incredibly good programmers with 20-50 years experience each who all wrote in typed languages for years and then couldn't be happier not to have types when they moved to Ruby. So ya, I dunno what I'm expecting here, but mostly offering my perspective.
So I say the "convincing" part a bit lightly -- I know that people must come to their own conclusions, I can only suggest the idea. People in both typed and untyped languages can be incredibly productive (my friend is much like you, so productive and disciplined that types don't seem to add much), and I can admit that adding types requires more up front thought (good) and more ceremony (~bad, especially if you don't have type inference).
Maybe the big problem is that the really good implementation of types is in languages that can be hard to approach (Haskell, OCaml, other ML langauges). The second best IMO is Rust but that has the whole separate ramp of ownership/borrowing which is hard for different reasons. Go has some good concepts (protocols) but a bunch of other warts.
I find that writing software with a good type system gives me confidence that the code is correct. It's almost axiomatic because you're building the structures that are being checked/used by the code, but nevertheless I have that much more confidence in a Typescript codebase or a Haskell codebase than an equivalent untyped codebase.
Basically I think like this:
- If I want correct, I write Haskell
- If I want quick, I write Typescript
- If I want performance, correctness and efficiency, I write Rust
Also, even if you don't believe me at all, there is a trend -- python adding typing, sorbet and other systems for ruby, typescript gaining popularity, etc. The crowd sometimes has some wisdom.