- We use
coulombfor unit analysis. coulombuses static types to represent units and this information doesn't make it to runtime.- Sometimes we do need units to be represented at runtime. For example, we may want want to store what units a user chose.
- This package attempts to reuse
coulombunit definitions to provide runtime units representation.
- This package attempts to reuse
- In
coulomb, aQuantity[N, U]is a wrapper over avalue: Nand the units (U) information is lost at runtime:
scala> import coulomb._, coulomb.si._, lucuma.core.math.dimensional._
scala> val velocity = 10.withUnit[Meter %/ Second]
val velocity: coulomb.Quantity[Int,coulomb.si.Meter %/ coulomb.si.Second] = Quantity(10)- In
dimensional, aMeasure[N]is similar but with a units represented in a value rather than in a type:
scala> val velocityM = velocity.toMeasure
val velocityM: lucuma.core.math.dimensional.Measure[Int] = Measure(10 m/s)- A
Measurecan optionally have an error interval:
scala> val velocityME = velocityM.withError(1)
val velocityME: lucuma.core.math.dimensional.Measure[Int] = Measure(10 ± 1 m/s)
scala> val velocityM2 = velocityME.exact
val velocityM2: lucuma.core.math.dimensional.Measure[Int] = Measure(10 m/s)-
The runtime representation of units of measure is the class
Units.Unitsis not type-parametrized. -
UnitOfMeasure[U]is a subtype ofUnitswhich is type-parametrized on the unit typeU.- It can be used to automatically derive a
Unitsinstance for a typeUwhich has an instance ofcoulomb'sBaseUnit[U],DerivedUnit[U, D], or a compound combination of these by using the%*,%/and%^combinators:
- It can be used to automatically derive a
scala> val units: Units = UnitOfMeasure[Meter %/ Second]
val units: lucuma.core.math.dimensional.Units = m/s- If we have a
Units, we can create aMeasureby usingwithValue:
scala> val measure = units.withValue(10)
val measure: lucuma.core.math.dimensional.Measure[Int] = Measure(10 m/s)- Sometimes we want to group units together. We can do this by assigning them a type tag via implicit
TaggedUnit[U, T]instances:
scala> trait Velocity // This could be a phantom type
scala> implicit val MPerS_Velocity = new TaggedUnit[Meter %/ Second, Velocity]
scala> val velocityMTagged: Measure[Int] Of Velocity = velocity.toMeasureTagged
val velocityMTagged: lucuma.core.math.dimensional.Of[lucuma.core.math.dimensional.Measure[Int],Velocity] = Measure(10 m/s)This is usually not necessary. coulomb allows us to define conversions between units, and we don't need to explicitly define abstract quantities (like velocity). However, in some cases like brightness, we may want to group together units that are not readily convertible among them without further information.
Unitscan be tagged too. We can access the taggedUnitsof a taggedMeasureby using theunitsTaggedlens:
scala> val unitsTagged: Units Of Velocity = Measure.unitsTagged.get(velocityMTagged)
val unitsTagged: lucuma.core.math.dimensional.Of[lucuma.core.math.dimensional.Units,Velocity] = m/s- If we have a
Units Of Tag, we can create aMeasure Of Tagby usingwithValueTagged:
scala> val velocityMTagged2: Measure[Int] Of Velocity = unitsTagged.withValueTagged(20)
val velocityMTagged2: lucuma.core.math.dimensional.Of[lucuma.core.math.dimensional.Measure[Int],Velocity] = Measure(20 m/s)coulombsupports numeric operations betweenQuantityinstances (eg: order, addition, negation, etc.) by relying on a set of typeclasses (UnitOrd,UnitAdd, etc.).Measuredoes not support this for the moment, but it could probably be implemented if needed, reusingcoulomb's typeclasses. Note that we would also have to deal witherror.