Two possible approaches spring to mind.

One thought is to take the second approach, but supply each prefix an instance of Num and rely on fromIntegral whenever you mix values of different units. This has the benefit of simplicity and recovering Num sugar.

Another thought is to define a meet semi-lattice of measures such that if one measure subsumes the other. i.e. I can define everything with a K that I can with an M (assuming you use Integer rather than Int). then that measure is lower than the other in the lattice. Then take the meet of the two values you supply to (.+./.-.) in the lattice.

Since KiB and K can only be added by lowering to your unprefixed representation IB. Then you get V shaped meet semi-lattice:

GiB -> MiB -> KiB -> IB <- K <- M <- G

and you can add a chain dangling down from your IB for smaller units, Deci, Centi, Milli, Micro, Nano, etc to obtain a Y shaped figure.

And you can define the semi-lattice at the type level with functional dependencies so that you get forward type inference to figure out the type of the lvalue of an assignment at least.

For large type-level (semi)lattices like this, I tend to just let template haskell define all of the instances for me, because you’ll wind up with a number of instances equal to the square of the number of prefixes you define unless you exploit the structure of the lattice.

]]>