bjornbm / dimensional

Dimensional library variant built on Data Kinds, Closed Type Families, TypeNats (GHC 7.8+).
BSD 3-Clause "New" or "Revised" License
102 stars 15 forks source link

Preferred way to for users to define custom units? #181

Closed bjornbm closed 6 years ago

bjornbm commented 7 years ago

What is the preferred/intended way for library users to define custom units? It used to be (pre 1.0):

mas :: Floating a => Unit DPlaneAngle a
mas = prefix 0.001 arcsecond

(A bit contrived as one would have used mas = milli arcsecond in this case, but see also #180.)

Based on how units are defined in the library and what is exported my best guess is to do something like:

import Numeric.Units.Dimensional.UnitNames (atom)

…

mas :: Floating a => Unit 'NonMetric DPlaneAngle a
mas = mkUnitR (atom "mas" "mas" "milliarcsecond") 0.001 arcsecond

(Note that I have not looked up the "correct" names, if any exist.)

Is the above the preferred way to define units? What did you intend @dmcclean? Is there a convenience function for "throwaway" units? Example:

-- | @timeAngle hours minutes seconds@ interprets the given time as an
  -- angle, where 24 h corresponds to 360°.
timeAngle :: Floating a => a -> a -> a -> Angle a
timeAngle h m s = h *~ hour + m *~ minute + s *~ second
  where
    hour   = mkUnitR (atom "" "" "") (1 Prelude./ 24) revolution
    minute = mkUnitR (atom "" "" "") (1 Prelude./ 60) hour
    second = mkUnitR (atom "" "" "") (1 Prelude./ 60) minute
ghost commented 7 years ago

It seems there are functions for units constructed of different base numeric types:

Combined with atom, and a base unit (siUnit or something like kilo gram), these seem sufficient.

Not sure if they're correct, but they work.

dmcclean commented 7 years ago

These were the ones I intended, but certainly am open to suggestions on a better way to do it.

bjornbm commented 6 years ago

My remaining question is whether there is, or should be, a more convenient way to not specify a name for a throw-away unit? In the above example I used mkUnitR (atom "" "" "") while in #128 @dmcclean used a hypothetical someName.

Once we have this answer I propose adding a section on creating custom units to the README.

dmcclean commented 6 years ago

I'm unclear on this use case. Isn't the defining essence of a unit that it is a named quantity? If it doesn't have a name, isn't it just a quantity? What can I do with an anonymous throw-away unit that I can't do with a quantity?

bjornbm commented 6 years ago

What can I do with an anonymous throw-away unit that I can't do with a quantity?

Convert a number to a quantity? But fair enough, I guess that instead of defining the throw-away units I should rewrite my example function as, e.g.:

-- | @timeAngle hours minutes seconds@ interprets the given time as an
  -- angle, where 24 h corresponds to 360°.
timeAngle :: Floating a => a -> a -> a -> Angle a
timeAngle h m s = hours h + minutes m + seconds s
  where
    days    = (*~ revolution)
    hours   = days    . (Prelude./ 24)
    minutes = hours   . (Prelude./ 60)
    seconds = minutes . (Prelude./ 60)