Live data from Hacker News

Use Your Type System

dzombak.com

121–130 of 357 posts

Re: Use Your Type System

#122
post #24

Unfortunately, this can be somewhat awkward to implement in certain structural typed languages like TypeScript. I often find myself writing something along the lines of type UserID = string & { readonly __tag: unique symbol } which always feels a bit hacky.

I never understood why people are so keen to do that in TypeScript. With that definition a `UserID` can still be silently "coerced" to a `string` everywhere. So you only get halfway there to an encapsulated type.

I think it's a much better idea to do:

    type UserID = { readonly __tag: unique symbol }
Now clients of `UserID` no longer knows anything about the representation. Like with the original approach you need a bit of casting, but that can be neatly encapsulated as it would be in the original approach anyway.

Re: Use Your Type System

#123
post #25

Earlier quoted context omitted.

Yep. For this reason, I wish more languages supported bound integers. Eg, rather than saying x: u32, I want to be able to use the type system to constrain x to the range of [0, 10). This would allow for some nice properties. It would also enable a bunch of small optimisations in our languages that we can't have today. Eg, I could make an integer that must fall within my array bounds. Then I don't need to do bounds ch…

This can be done in typescript. It’s not super well known because of typescripts association with frontend and JavaScript. But typescript is a language with one of the most powerful type systems ever. Among the popular languages like golang, rust or python typescript has the most powerful type system. How about a type with a number constrained between 0 and 10? You can already do this in typescript. type onetonine =…

Typescript's type system is turing complete, so you can do basically anything with it if this sort of thing is fun to you. Which is pretty much my problem with it: this sort of thing can be fun, feels intellectually stimulating. But the added power doesn't make coding easier or make the code more sound. I've heard this sort of thing called the "type puzzle trap" and I agree with that.

I'll take a modern hindley milner variant any day. Sophisticated enough to model nearly any type information you'll have need of, without blurring the lines or admitting the temptation of encoding complex logic in it.

Re: Use Your Type System

#124
post #37

An adjacent point is to use checked exceptions and to handle them appropriate to their type. I don't get why Java checked exceptions were so maligned. They saved me so many headaches on a project where I forced their use as I was the tech lead for it. Everyone hated me for a while because it forced them to deal with more than just the happy path but they loved it once they got in the rhythm of thinking about all the…

I rarely have more than handful of try..catch blocks in any application. These either wrap around an operation that can be retried in the case of temporary failure or abort the current operation with a logged error message.

Checked exceptions feel like a bad mix of error returns and colored functions to me.

Re: Use Your Type System

#125

Swift has a typealias keyword but it's not really useful for this since two distinct aliased types with the same underlying type can be freely interchanged. Wrong code may look wrong but it will still compile. Wrapper structs are the idiomatic way to achieve this, and with ExpressibleByStringLiteral are pretty ergonomic, but I wonder if there's a case for something like a "strong" typealias ("typecopy"?) that indicat…

Yeah, most languages I've used are like this. E.g. rust/c/c++.

I guess the examples in TFA are golang? It's kind of nice that you don't have to define those wrapper types, they do make things a bit more annoying.

In C++ you have to be extra careful even with wrapper classes, because types are allowed to implicitly convert by default. So if Foo has a constructor that takes a single int argument, then you can pass an int anywhere Foo is expected. Fine as long as you remember to mark your constructors as explicit.

Re: Use Your Type System

#126

Swift has a typealias keyword but it's not really useful for this since two distinct aliased types with the same underlying type can be freely interchanged. Wrong code may look wrong but it will still compile. Wrapper structs are the idiomatic way to achieve this, and with ExpressibleByStringLiteral are pretty ergonomic, but I wonder if there's a case for something like a "strong" typealias ("typecopy"?) that indicat…

In what precise way are you envisioning that this would be different from a wrapper struct?

Re: Use Your Type System

#127
In TypeScript you can enable this by using BrandedTypes like this:

  type UserId = string & { readonly __tag: unique symbol };

In Python you can use `NewType` from the typing module:

  from typing import NewType
  from uuid import UUID

  UserId = NewType("UserId", UUID)

Re: Use Your Type System

#128
post #42

This technique makes me sad. Not because it's a bad idea. Quite the contrary. I've sung the praises of it myself. But because it's like the most basic way you can use a type system to prevent bugs. In both the sense used in the article, and in the sense that it is something you have to do to get the even more powerful tools brought to bear on the problem that type systems often. And yet, in the real world, I am const…

I think if it were better supported in the majority of strictly typed programming languages then it would be used more. Most languages make it a big hassle.

Re: Use Your Type System

#129

Earlier quoted context omitted.

I've recently been following red-green-refactor but instead of with a failing test, I tighten the screws on the type system to make a production-reported bug cause the type checker to fail before making it green by fixing the bug. I still follow TDD-with-a-test for all new features, all edge cases and all bugs that I can't trigger failure by changing the type system for. However, red-green-refactor-with-the-type-syst…

I found myself following a similar trajectory, without realizing that’s what I was doing. For a while it felt like I was bypassing the discipline of TDD that I’d previously found really valuable, until I realized that I was getting a lot of the test-first benefits before writing or running any code at all. Now I just think of types as the test suite’s first line of defense. Other commenters who mention the power of t…

I dont think tests and types are the same "thing" per se - they work vastly better in conjunction with each other than alone and are weirdly symmetrical in the way that theyre bad substitutes for each other.

However, Im convinced that theyre both part of the same class of thing, and that "TDD" or red/green/refactor or whatever you call it works on that class, not specifically just on tests.

Documentation is a funny one too - I use my types to generate API and other sorts of reference docs and tests to generate how-to docs. There is a seemingly inextricable connection between types and reference docs, tests and how-to docs.

Re: Use Your Type System

#130

Swift has a typealias keyword but it's not really useful for this since two distinct aliased types with the same underlying type can be freely interchanged. Wrong code may look wrong but it will still compile. Wrapper structs are the idiomatic way to achieve this, and with ExpressibleByStringLiteral are pretty ergonomic, but I wonder if there's a case for something like a "strong" typealias ("typecopy"?) that indicat…

Yeah, most languages I've used are like this. E.g. rust/c/c++. I guess the examples in TFA are golang? It's kind of nice that you don't have to define those wrapper types, they do make things a bit more annoying. In C++ you have to be extra careful even with wrapper classes, because types are allowed to implicitly convert by default. So if Foo has a constructor that takes a single int argument, then you can pass an i…

Both clang-tidy and cpplint can be configured to require all single-argument constructors (except move, copy, and initializer-list constructors) to be marked explicit, in order to avoid this pitfall.
Post reply on HN