openlang

Units of measure as cheap ids

For integer ids I've been trying units of measure instead of single-case unions, and I'd like a second opinion before it spreads. CustomerId of int allocates unless it's marked [<Struct>], and every use goes through a constructor or a match. With [<Measure>] type customerId the id is int<customerId>, the unit is erased at run time, and passing an int<orderId> where a customer id is expected fails to compile.

The costs I've found so far are small but real. Only numeric types take a unit, so string ids still need a union. Getting a plain number back means int id, which reads fine. And a reflection-based JSON serializer sees a plain int, so the unit is reapplied by hand on the way in, which is exactly the spot where a mixup could slip through.

Maybe units for internal ids and a union at the edges where values come from outside? I'm not sure the split is worth explaining to the next reader.

后续讨论 1

顺着彼此的依据读下去
  1. 固定链接

    One thing the unit lets through that the union doesn't. Arithmetic.

    [<Measure>] type customerId
    
    let a = 41<customerId>
    let b = 1<customerId>
    let c = a + b   // int<customerId>, compiles

    Adding two customer ids type-checks and produces a third customer id, and a * b gives int<customerId ^ 2>. Units were made for quantities, so all the usual arithmetic stays available. CustomerId of int has no + unless someone writes one, and a pattern like let (CustomerId n) = id makes the place where the number comes out visible. For ids that only get compared and passed around, I'd lean toward the union, with [<Struct>] for the allocation worry.

同一个问题的其他角度

  1. Annotation or satisfies for a config object

    For config objects I've mostly moved from const config: Config = {...} to {...} satisfies Config. With the annotation, config…

    回复 6最近活动 typescript
  2. Does an empty palette need its own case?

    An empty color list means two different things in a swatch editor, and Save should be able to tell them apart. Color validity…

    回复 8最近活动 fsharphaskell
  3. Is a read-only List<Int> a snapshot?

    List<Int> is a read-only view, not a snapshot. It tells a consumer which operations it can call through that reference. It pr…

    回复 4最近活动 kotlin