3 ms·
The Unreasonable Effectiveness of Naming Integers
- PreInternet01 2y agoYou can do this in C# as well, so that the final line in the snippet below causes a compile-time error, which is quite nice for many purposes: private enum Derp { } private enum Foo { } var derp = (Derp)12; var foo = (Foo)99; foo = derp; However, this is definitely a hack, and I sort-of feel the same about the Zig solution from the article. Would be nice if languages had 'cleaner' support for this?
- gbin 2y agoIn rust it would be: struct Foo(u32); No?
- neonsunset 2y agoIn C#, a now popular choice is record struct Foo(int Value); In F#, this is instead done with // Foo and Bar cannot be assigned to each other type Foo = Foo of int type Bar = Bar of int // Foo and Bar can be assigned to each other type Foo = int type Bar = int The first variant uses single-case unions. It's a bit unfortunate that they also default to classes over structs unless you annotate them with [<Struct>], partially fixed by escape analysis though. The enum trick is the tersest and will also serialize correctly in most cases, but I have never seen anyone use it like that before.
- resonious 2y agoWhat would be your preferred syntax?
- PreInternet01 2y agoWhen using implicit typing, definitely `var foo = (int Foo)12`. Less sure what the explicit variant should look like, since parsing may be trickier, but `(int Foo) foo = 12` might work?
- noelwelsh 2y agoThis is what is known as new types or opaque types in other languages, right? The runtime representation is that of the "wrapped" type, but they are distinguished at compile-time.
- exceptione 2y agoIndeed, this seems to be just a case of newtypes. Newtypes are effective for any kind of value, not just numbers. newtype PostalCode = PostalCode String newtype LicenseKey = LicenseKey String Now you won't be accidentally mixing postal codes and license keys. Every language should support newtypes (and algebraic types).
- Cthulhu_ 2y agoIt's definitely valuable in the domain layer of a business application, Typescript and Go support it as well.
- a_t48 2y agoIn a past job I implemented these with heavy use of C++ operator overrides. Not the prettiest option, but it stopped the six id/index types I was using from accidentally getting misused. Something like - ObjectId, Resource Id, User Id, Group Index, some other things. This was for the ingame handling for https://store.sansar.com/ https://store.sansar.com/ - it's a pity the project kind of died, we had some neat ideas for payment for providence. The idea thrown around was that "if I make some wheels and put them on the marketplace, you can make a car, sell it, and I'd get a cut of every car sale". This was of course, ignoring the "analog hole" of someone figuring out how to export/reimport your asset.
- lynx23 2y agoI am not an active Zig guy, so... YYMV, but... To me, the examples are actually not self explanatory. What does the _ do? How are these constants used? Why is it something special? C had enums since forever, what is the novelty here?
- SAI_Peregrinus 2y agoC has a rather weak type system: using one enum type in a function that takes a different enum isn't guaranteed to be an error, instead they promote to an integer type and silently convert (though you can set up most compilers to make this an error, e.g. `-Wenum-conversion` and `-Werror` for GCC & Clang). The basic idea is the "newtype" pattern: different types may have exactly the same valid set of values, but not be interchangeable. E.g. kilograms and US customary pounds, or meters and miles. If you've got a function `f(distance: double)` where `distance` needs to be in kilometers but your program also handles miles, it's nice to be able to define a type for kilometers that's different from the type for miles so the function won't compile if passed in miles incorrectly. So you get `f(distance: kilometers)`. That also stops someone passing in pounds to `f`, or any other such nonsense.
- weinzierl 2y agoThe real power comes, when you can express: var age: 18 ... 120; This is more than 50 years old Pascal. As far as I know the only mainstream language that supports this in its type system (and compile-time checked) today is Ada[1]. Blows my mind. I never got over the loss when I switched to C. [1] I think you could convince Rust to do something pretty close but a bit uglier syntax-wise.
- xigoi 2y agoNim also supports this. var age: range[18..120]
- deleted 2y ago[deleted]
- bjourne 2y agoYou can accomplish the same thing in C with some macros: https://stackoverflow.com/a/376478/189247 https://stackoverflow.com/a/376478/189247 But honestly, it is not worth the bother. You just end up with lots of (my_index_type_is_different_from_yours_t) casts littered in your code.