I'm personally tired of staring at variables trying to figure out what they're supposed to be, then having to dive into source to see how its used. C/C++/C# solved that problem, why are we still dealing with it?
How does nobody(?) support this yet?
Python supports some parts: you can subclass int, and you get all of the int methods like addition and subtraction for free, but "distanceInKm + distanceInKm" gives you an int instead of a distanceInKm; and "distanceInKm + distanceInMiles" gives you an int instead of an error.
Rust also has partial support but from the other end: distanceInMiles and distanceInKm can be two distinct subclasses of int, and adding them together is a compile time error. But also adding distanceInMiles with distanceInMiles is a compiler error, because these are basically "completely new classes" rather than "subclasses of int", and so you have to implement add / subtract / stringify / etc for yourself for every type D: (I'm fairly new to Rust so if there is a shortcut there that I'm missing please do point it out)
newtype Pixel = Pixel Int
newtype Em = Em Int
pixelWidthToEm :: Pixel -> Em
pixelWidthToEm (Pixel px) = Em px
You can try to call `pixelWidthToEm` with anything other than pixels and it won't work.More dynamically, with an open type variable that only exists in the type system:
data User a =
User { name :: String, socialSecurityNumber :: String }
data LogSafe
data LogUnsafe
logUser :: User LogSafe -> IO ()
logUser = undefined
makeUserLogSafe :: User LogUnsafe -> User LogSafe
makeUserLogSafe = undefined
We can never log the user unless the user is deemed LogSafe and we make functions that produce log safe users that you have to call before hand, in order to make sure that sensitive data isn't printed to logs.These are things that have been around for a long time in almost every type system, but people's general lack of interest in using type systems to help them conspires to keep them in the dark.
Here's how you can create a number type distinct from other number types in TypeScript:
type DistanceInPixels = number & { readonly __newtype__: "DistanceInPixels" }
And a type alias that allows you to create them: export type Newtype<T, Tag extends string> = T & { readonly __newtype__: Tag }
type Pixels = Newtype<number, "Pixels">In any case, to answer the question of "How does nobody(?) support this yet?", have you heard of https://frinklang.org/ ? It's not a useful tool for most codebases I work on but it's an interesting idea.
Depending on your type system, a well-typed program can eg run faster, because the compiler / interpreter can elide certain runtime safety checks that would be necessary in untyped code.
If your type system is crazy enough, you can even track the runtime complexity of your program at the type level, including whether your program runs in finite time. See eg Dhall (https://dhall-lang.org/) whose type systems only allows programs running in finite time.
Generally, there is this huge disconnect between how code is expressed as text and how it is handled in as a graph structure inside the tooling. It is soon time to move beyond simple text files for code, I believe.
Depending on what you want to do, you might also want to start with the types and have the computer figure out the implementation.
> It might even not show you types as code at all and by default and overlay/add this info only on request.
Types annotations are often are great documentation, and there's a lot of practical knowledge in the ML communities about what types annotations to show in the source for helping with understand and debugging and which ones to leave out as clutter.
I wrote 'proper type inference' above, because it's much more powerful than the watered down version Go and C++ give you. See eg https://news.ycombinator.com/item?id=8447280 for a Rust example.
But honestly, if you're asking your IDE to do it, that means you're asking your IDE to do static analysis of your code - and type-checking in a lot of ways is just another static analysis technique. And for a lot of us (myself included) we prefer to catch as many of these bugs as possible using static analysis, instead of waiting for someone to get paged when it causes an outage.
Yes, there's a trade-off, and types can be obnoxious (Java imports being probably one of the worst offenders, C++11 introduced `auto` for a reason), but that's the cost we pay.
So, please, be brave and evaluate a 100kloc codebase+deps that may contain a top level `rm -rf ~/`
Now the JS fanboys discovered and moved (from Coffeescript to Babel ESxx) to Typescript they apparently think that they can write beautiful and bugfree software just because of static type checking! Let them please move to C++ or whatever statically typed language and shoot themselves in the foot by making all those mistakes that have nothing to do with static type checking at all! Oh, and of course hitting the wall because they are missing their precious 'any' keyword!
I totally agree that type checking for dynamic languages should be done in the IDE, tooling. But static typing in the dynamic language world is a hype at the moment, so we'll have to go through a wave static type checking frenzy. For my work I look at horrible code bases, perfectly typed and strictly formatted by tslint..
> For my work I look at horrible code bases, perfectly typed and strictly formatted by tslint.
There is no language that can stop people from producing horrible code.
That's bold. Do you think no developer would be able to manage it without TS? In that case you must be a fanboy!
And honestly, are you not using 'any'? And do you think your app cannot crash because of a type error at runtime? And do you trust all the third party libraries you are using that they always provide you with consistent types, also during runtime? I ask this because most TS proponents live in some kind of dream.
1. I don't have the mental capacity to keep every single function's argument/return shape in my head, or to manually check it every time I make a change. Unit tests can't deliver 100% code coverage in practice.
2. Nor do I want to perform refactorings with stone age tools like s/setFoo/setBar/g. Setting up type information lets my IDE understand which calls to .push() deal with a native Array and which ones deal with my own class, so it can rename the latter ones when I ask. I can also use tools like "Find References" and avoid false positives.
3. I'm not a one-man-band. My coworkers need to deal with this project too, and new developers need to be introduced to it from time to time, and types serve as documentation and guard rails for them much better than jsdoc or regular comments. (This also serves as a significant barrier against using anything more esoteric like Elm, because nobody around would be familiar with it. TypeScript adds just enough syntax on top of regular JS to keep JS users comfortable.)
---
I do use `any` (and `unknown`), I have no doubts that an edge case can crash my app because I didn't validate something, and I never expect third-party code to work flawlessly whether it has types or not. Rejecting TS completely because "but run time loopholes" is throwing the baby out with the bathwater (or to put it in a more hyperbolic way, being an anti-vaxxer because vaccines are not 100% safe from side-effects). TS and types are an additional safety net/force multiplier†, not a silver bullet. (That said, what is a silver bullet? Because I'd sure like one.)
---
† Only applies to a project that has passed its initial rapid prototyping phase. During wild prototyping rides, types can indeed slow you down. But that's really the same debate as RDBMS vs NoSQL.