4 ms·
I couldn't care less about mathematics, but I do care about making impossible state impossible and types documenting the domain. If you type some state as:
by epolanski 1y ago
I couldn't care less about mathematics, but I do care about making impossible state impossible and types documenting the domain.
If you type some state as:
isLoading: boolean
result: Foo
hasError: boolean
errorMessage: string | null
then you're creating a giant mess of a soup where the state of your program could have a result, be loading and an error at the same time. If you could recognise that the state of your program is a sum of possible states (loading | success | error), and not their product as the type above you could highly simplify your code, add more invariants and reduce the number of bugs.
And that is a very simple and basic example, you can go *much* further, as in encoding that some type isn't merely a number through branded types, but a special type of number, be it a positive number between 2 and 200 or, being $ or celsius and avoiding again and entire class of bugs by treating everybody just as an integer or float.
- what 1y ago>encoding that a number is between 2 and 200 What’s the point of this level of autism when you still have to add run time checks?
- kennywinker 1y agoFor a function setVelocity() that can accept 1..<200. You call it with numbers that you enter directly and types tell you something that would otherwise be a comment on the function, or you do runtime checks elsewhere, and the type becomes proof that you checked them before handing it into the function. Btw, using “autism” to mean “pedantry” leaves a bit of a bad taste in my mouth. Maybe you could reconsider using it that way in the future.
- samdoesnothing 1y agoPushing everything to types like this creates a different burden where you're casting between types all over the place just to use the same underlying data. You could just clamp velocity to 200 in the callee and save all that hassle.
- kennywinker 1y agoCasting? Not really - i think you’d only need a couple type checks. Imo this is mostly useful for situations where you want to handle input validation (and errors) in the UI code and this function lives far away from ui code. Your point about clamping makes sense, and it’s probably worth doing that anyway, but without it being encoded in the type you have to communicate how the function is intended to be used some other way.
- samdoesnothing 1y agoHow would you convert a Number type to a ClampedNumber type without casting?
- kennywinker 1y agoAh, yeah you’re right. I somehow thought typescript could do type narrowing based on checks - like say: If (i >= 1) { // i’s type now includes >= 1 } But that is not the case, so you’d need a single cast to make it work (from number to ClampedNumber<1,200>) or however exactly you’d want to express this. Tbf having looked more closely into how typescript handles number range types, I don’t think I would ever use them. Not very expressive or clear. I think I hallucinated something closer to what is in this proposal: https://github.com/microsoft/TypeScript/issues/43505 https://github.com/microsoft/TypeScript/issues/43505 I still think that the general idea of communicating what acceptable input is via the type system is a good one. But the specifics of doing that with numbers isn’t great in typescript yet.
- samdoesnothing 1y agoHow would you implement it in other languages that support it better? Can you literally just do a range check and the compiler infers its range for types? If so thats actually pretty neat.
- skydhash 1y agoWhat about documentation as in docstring, function signature label and the like. I quite like CommonLisp, EmacsLisp, Go for that.
- epolanski 1y agoBeing guaranteed that _at runtime_ you won't end up with 1 or 201.
- samdoesnothing 1y agoYou can encode that "correctly" in pure JS class LoadingState extends State {} class ResultState extends State {} class ErrorState extends State {} const state: State = new LoadingState Generally it's a bad pattern to have dependant fields like your example, but you don't need TS to solve it.