Earlier quoted context omitted.
And that's my point: I'm usually getting AccountIDs from strings (passed in via HTTP requests) so the whole thing becomes a pointless exercise.
Do you validate them? I assume you do. Feels like a great time to cast them too
Use Your Type System
221–230 of 357 posts
Re: Use Your Type System
#222This reminds me of the mp-units [1] library which aims to solve this problem focusing on the physical quantities. The use of strong quantities means that you can have both safety and complex conversion logic handled automatically, while having generic code not tied to single set of units. I have tried to bring that to the prolog world [2] but I don't think my fellow prolog programmers are very receptive to the idea ^…
I remember a long, long time ago, working on a project that handled lots of different types of physical quantities: distance, speed, temperature, pressure, area, volume, and so on. But they were all just passed around as "float" so you'd every so often run into bugs where a distance was passed where a speed was expected, and it would compile fine but have subtle or obvious runtime defects. Or the API required speed i…
Re: Use Your Type System
#223I've used the approach described for uuids on a project and I liked it. We were using typescript so we went further using template literal types [1] type UserId = `user:${uuid}`; type OrgId = `org:${uuid}`; This had the benefit that we could add validation (basic begins with kind of logic) and it was obvious upon visual inspection (e.g. in logs/debugging). 1. https://www.typescriptlang.org/docs/handbook/2/template-li…
I assume you used these against a relational database? Did you commit those ids with the prefix still attached? or did you `.split()[1]` or something? I think it's a pretty good idea. I'm just wondering how this translated to other systems.
The only drawback was marshalling the types when they come out of the db layer. Since the db library types were string we had to hard cast them to the correct types, really my only pain. That isn't such a big deal, but it means some object creation and memory waste, like:
// pseudo code:
const results = dbclient.getObjectsByFilter( ... );
return results.map(result => ({
id: result.id as ObjectId,
...
}));
We normally didn't do it, but it would be at that time you could have some `function isObjectId(id:string) : id is ObjectId { id.beginsWith("object:"); }` wrapper for formal verification (and maybe throw exceptions on bad keys). And we were probably doing some type conversions anyway (e.g. `new Date(result.createdAt)`).If we were reading stuff from the client or network, we would often do the verification step with proper error handling.
Re: Use Your Type System
#224An 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 also think its a bit cleaner to have a nicely pattern matched handler blocks than bespoke handling at every level. That said, if unwrapped error results have a robust layout then its probably pretty equivalent.
Re: Use Your Type System
#225Earlier 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…
You can do this quite easily in Rust. But you have to overload operators to make your type make sense. That's also possible, you just need to define what type you get after dividing your type by a regular number and vice versa a regular number by your type. Or what should happen if when adding two of your types the sum is higher than the maximum value. This is quite verbose. Which can be done with generics or macros.
Re: Use Your Type System
#226In C#, I often use a type like: readonly struct Id32 { public readonly int Value { get; } } Then you can do: public sealed class MFoo { } public sealed class MBar { } And: Id32 x; Id32 y; This gives you integer ids that can’t be confused with each other. It can be extended to IdGuid and IdString and supports new unique use cases simply by creating new M-prefixed “marker” types which is done in a single line. I’ve als…
Re: Use Your Type System
#227Earlier 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…
in raku, that’s spelled subset OneToTen of Int where 1..10:
Re: Use Your Type System
#228from typing import NewType
UserId = NewType('UserId', int) some_id = UserId(524313)
Re: Use Your Type System
#229Earlier quoted context omitted.
The full-blown version that guarantees no bounds-check errors at runtime requires dependent types (and consequently requires programmers to work with a proof assistant, which is why it's not very popular). You could have a more lightweight version that instead just crashes the program at runtime if an out-of-range assignment is attempted, and optionally requires such fallible assignments to be marked as such in the c…
AIUI WUFFS doesn't need a full blown proof assistant because instead of attempting the difficult problem "Can we prove this code is safe?" it has the programmer provide elements of such a proof as they write their program so it can merely ask "Is this a proof that the program is safe?" instead.
Re: Use Your Type System
#230I've used the approach described for uuids on a project and I liked it. We were using typescript so we went further using template literal types [1] type UserId = `user:${uuid}`; type OrgId = `org:${uuid}`; This had the benefit that we could add validation (basic begins with kind of logic) and it was obvious upon visual inspection (e.g. in logs/debugging). 1. https://www.typescriptlang.org/docs/handbook/2/template-li…
But depending on the format it can sometimes be tricky to narrow a string back down to that format.
We have type guards to do that narrowing. (see: https://www.typescriptlang.org/docs/handbook/2/narrowing.htm..., but their older example is a little easier to read: https://www.typescriptlang.org/docs/handbook/advanced-types....)
If writing the check is too tricky, sometimes it can just be easier to track the type of a value with the value (if you can be told the type externally) with tagged unions (AKA: Discriminated unions). See: https://www.typescriptlang.org/docs/handbook/typescript-in-5...
And if the formats themselves are generated at runtime and you can use the "unique" keyword to make sure different kinds of data are treated as separate (see: https://www.typescriptlang.org/docs/handbook/symbols.html#un...).
You can combine `unique symbol` with tagged unions and type predicates to make it easier to tell them apart.