Representing a “units” type or 1-D vector space

Is there a way to represent a type that is essentially a unit (alternatively, a 1-D vector space)? For example, I want to represent 500 kilometers, which supports (let’s say) addition and subtraction of other amounts of kilometers, and scalar multiplication and integer division by builtin integer types.

Clearly you can do it by putting the number of km in a struct, but you’d have to write your own add, subtract, etc; what I’d rather have is something where I get the syntactic sugar of having +, -, * “just work.”

// appropriately create the `km` type such that...
const x: km = 500;
const y: km = 20;
const z: km = x + y;
const my_int: u32 = 30;
const k: km = my_int * x; // should work
const l: f32 = x / y;
// const w = x + my_int; should fail at compile time

Zig currently cannot create distinct types that inherits all the operations.
To fulfill the requirement your only option is to use a struct and have add, mul, etc. functions.

3 Likes

This is an unexpectedly difficult problem. Nominally-distinct primitive types might happen (they aren’t on the roadmap), but it’s unlikely Zig will go the full distance to properly support units in the type system, it adds a lot of complexity to get it right.

2 Likes

You might be interested in F# units of measure: Units of Measure - F# | Microsoft Learn

2 Likes

I think the notion of “unit” is not enough.
“Unit” is a name for some particular value of a physical quantity (“km” etc).
Are are not adding “names”, we are adding values of physical quantities.

Just some simple exercise:

const Quantity = enum {
    distance,
};

const Unit = enum {
    m, km
};

// also need some Quantity-Unit "compatibility"/"affinity"

const Value = struct {

    q: Quantity = undefined,
    u: Unit = undefined,
    v: f32 = undefined,

    fn add(v1: Value, v2: Value) !Value {
        if (v1.q != v2.q) return error.QuantityMismatch;
        if (v1.u != v2.u) return error.UnitMismatch; // or do scaling
        return .{.q = v1.q, .u = v1.u, .v = v1.v + v2.v};
    }
};

const p = @import("std").debug.print;

pub fn main() !void {
    const d1 = Value{.q = .distance, .u = .m, .v = 500};
    const d2 = Value{.q = .distance, .u = .km, .v = 1};
    p("d1 = {any}\n", .{d1});
    p("d2 = {any}\n", .{d2});
    const d3 = try Value.add(d1, d2);
    p("d3 = {any}\n", .{d3});
}

As intended, the program fails with UnitMismatch error.

1 Like

Nice, I’d never looked at F# before.