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 16 forks source link

Choose pattern for torsor newtypes #23

Open dmcclean opened 10 years ago

dmcclean commented 10 years ago

Temperatures and times present a few challenges. The SIUnits module deals with one of them by:

fromDegreeCelsiusAbsolute :: Fractional a => a -> ThermodynamicTemperature a
fromDegreeCelsiusAbsolute x = x *~ degreeCelsius + 273.15 *~ degreeCelsius
toDegreeCelsiusAbsolute :: Fractional a => ThermodynamicTemperature a -> a
toDegreeCelsiusAbsolute x = (x - 273.15 *~ degreeCelsius) /~ degreeCelsius

I think this is not ideal because it encourages people to store absolute temperatures in ThermodynamicTemperature a form.

Having newtype AbsoluteTemperature a = AbsoluteTemperature (ThermodynamicTemperature a) somewhere seems like a good idea from a documentation perspective, and also allows checking conversions between various absolute scales.

Similar issues arise when tracking times, but they are even worse because the universe wasn't nice enough to supply us with an easily accessible "absolute". As a result, code with precision timekeeping needs generally requires not one but several newtypes for absolute times.

It would be nice if the core dimensional package addressed these concerns (at least the temperature one) to facilitate interoperability among other libraries developed on top of it.

I'm not sure if this can be addressed parametrically, or if it is better addressed by just laying down a pattern to be copied at each monomorphic concept where it is needed.

One possible idea (reserving comment on its merits):

newtype DimensionalTorsor (d :: Dimension) (n :: Symbol) (a :: *) = Torsor (Quantity d a)
difference :: DimensionalTorsor d n a -> DimensionalTorsor d n a -> Quantity d a
offset :: DimensionalTorsor d n a -> Quantity d a -> DimensionalTorsor d n a

type AbsoluteTemperature = DimensionalTorsor DThermodynamicTemperature "AbsoluteTemperature"
type UtcTime = DimensionalTorsor DTime "UTC"
type TaiTime = DimensionalTorsor DTime "TAI"
-- and so forth

Unfortunately this doesn't provide an opportunity for validation/canonicalization, so for example uses of offset can result in negative AbsoluteTemperatures. (Which, while it might make sense in itself, does not make sense as being offset from a nearby positive temperature, AFAIUI.) Typeclass pixie dust could remedy this defect, if it was thought to be worth it.

(There is a related issue for angles, where there is a commonly needed newtype that treats Angle a values as equivalent when they are separated by an integer number of turns. Addressing that may or may not be done using similar techniques.)

bjornbm commented 10 years ago

This is an interesting proposal, certainly worth thinking about. I've been inclined to stick to roughly the level of “support” corresponding to the NIST guide (which doesn't discuss torsors). But I could consider adding something like this if we get the API right.

You can see how I have been treating absolute times (epochs) so far in https://github.com/bjornbm/astro/blob/master/Astro/Time.hs. Lines 110 to 130 correspond to the API in your “possible idea”.

dmcclean commented 10 years ago

Lines 133 through 139 could be stolen as well, those are good operator names.

But then again maybe having the one in dimensional would only help astro for interchange purposes on the concepts of time that are essentially classical. Because you also have a phantom type that represents the rate that time is advancing, if I understand it. Or sort of the place where the time is being measured. (Relativity loses me fairly quickly...)

dmcclean commented 10 years ago

Might be best just to provide an instance for AffineSpace from Data.AffineSpace. Settles the naming issue and builds on what already exists.

class AdditiveGroup (Diff p) => AffineSpace p where
  type Diff p
  (.-.)  :: p -> p -> Diff p
  (.+^)  :: p -> Diff p -> p

The required AdditiveGroup instance would be dimensionally polymorphic. So while you'd have to write one AffineSpace instance for each such newtype at least the AdditiveGroup part could be done once and for all.

newtype AbsoluteTemperature a = AbsoluteTemperature (ThermodynamicTemperature a)

instance (Num a) => AffineSpace (AbsoluteTemperature a) where
  type Diff (AbsoluteTemperature a) = ThermodynamicTemperature a
  (AbsoluteTemperature x) .-. (AbsoluteTemperature y) = x - y
  (AbsoluteTemperature x) .+^ y = AbsoluteTemperature $ x + y
bjornbm commented 10 years ago

I'm a little bit reluctant to add a dependency (see also #30).

Either way I think Torsors should go in their own module (Numeric.Units.Dimensional.Torsors), probably re-exported in the prelude once we are happy with the. You are welcome I draft a module if you want.

dmcclean commented 10 years ago

Getting started in the experimental package: https://github.com/bjornbm/dimensional-dk-experimental/blob/master/Numeric/Units/Dimensional/DK/AbsoluteTemperature.hs

dmcclean commented 10 years ago

It turns out that you can't do GeneralizedNewtypeDeriving for classes like AffineSpace that have an associated type. I discovered that while trying to make newtype TorsorQuantity d a = TorsorQuantity (Quantity d a) with a once-and-for-all AffineSpace instance. So I scrapped that idea and made the instance directly for AbsoluteTemperature.

bjornbm commented 10 years ago

So the idea is to just use the AbsoluteTemperature constructor if you are working with the Kelvin scale?

dmcclean commented 10 years ago

I was thinking absoluteZero .^+ myKelvinTemp and hiding the AbsoluteTemperature constructor, but that could work too.

bjornbm commented 10 years ago

Hiding the constructor makes good sense as AbsoluteTemperature (23 *~ degreeCelcius) doesn't mean what it says.

bjornbm commented 10 years ago

Do you have a feel for the relative merits of Conal's lib versus Ekmett's linear? (Except for number of dependencies.)

dmcclean commented 10 years ago

No, none whatsoever. I will look into it though.

dmcclean commented 10 years ago

linear's dependencies definitely seem to overlap better with my other needs.

Still not much of a clue about the merits. They seem very similar in this particular respect.

bjornbm commented 10 years ago

Ok. Well feel free to switch if you prefer linear.

dmcclean commented 10 years ago

vector-space defines a more general type for this kind of thing than linear does.

Excerpting vector-space:

class AdditiveGroup (Diff p) => AffineSpace p where
  type Diff p
  (.-.)  :: p -> p -> Diff p
  (.+^)  :: p -> Diff p -> p

class AdditiveGroup v where
  zeroV :: v
  (^+^) :: v -> v -> v
  negateV :: v -> v

Matching definitions from linear:

class Additive (Diff p) => Affine p where
  type Diff p :: * -> *
  (.-.) :: Num a => p a -> p a -> Diff p a
  (.+^) :: Num a => p a -> Diff p a -> p a
  (.-^) :: Num a => p a -> Diff p a -> p a

class Functor f => Additive f where
  zero :: Num a => f a
  (^+^) :: Num a => f a -> f a -> f a
  (^-^) :: Num a => f a -> f a -> f a
  lerp :: Num a => a -> f a -> f a -> f a
  liftU2 :: (a -> a -> a) -> f a -> f a -> f a
  liftI2 :: (a -> b -> c) -> f a -> f b -> f c

(Note the superclass context on Additive.) Because Quantity d isn't a Functor, that's the end of that, as far as I can see.

dmcclean commented 8 years ago

Question on this: There's absolute temperature. And absolute pressure. Is it true that those are the only dimensions with universal absolutes, or can you think of any others?

dmcclean commented 8 years ago

Perhaps there are a lot more? Power, luminous intensity (etc), amount of substance, quantities that you get by dividing those by area/volume/time?

bjornbm commented 8 years ago

Gravitational potential? Absolute rotation (and derived quantities such as angular momentum)? …

On 2016-09-07, at 16:09, Douglas McClean <notifications@github.com mailto:notifications@github.com> wrote:

Perhaps there are a lot more? Power, luminous intensity (etc), amount of substance, quantities that you get by dividing those by area/volume/time?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/bjornbm/dimensional/issues/23#issuecomment-245292099, or mute the thread https://github.com/notifications/unsubscribe-auth/AACRlPYC0Ql0eKsaqCeuHdZYhaQT8oRnks5qnsWHgaJpZM4BlU5z.