Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Choose pattern for torsor newtypes #23

Open
dmcclean opened this issue Feb 27, 2014 · 17 comments
Open

Choose pattern for torsor newtypes #23

dmcclean opened this issue Feb 27, 2014 · 17 comments

Comments

@dmcclean
Copy link
Collaborator

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
Copy link
Owner

bjornbm commented Feb 27, 2014

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Owner

bjornbm commented Mar 3, 2014

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
Copy link
Collaborator Author

dmcclean commented Mar 7, 2014

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 7, 2014

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
Copy link
Owner

bjornbm commented Mar 7, 2014

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

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 7, 2014

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

@bjornbm
Copy link
Owner

bjornbm commented Mar 7, 2014

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

@bjornbm
Copy link
Owner

bjornbm commented Mar 7, 2014

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

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 8, 2014

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

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 8, 2014

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

  • linear itself includes a type for quaternions
  • The netwire package that I am using for FRP shares the containers and semigroups dependencies.
  • Ed's ad package for automatic differentiation shares the reflection and containers dependencies.

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

@bjornbm
Copy link
Owner

bjornbm commented Mar 8, 2014

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

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 9, 2014

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
Copy link
Collaborator Author

dmcclean commented Sep 7, 2016

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
Copy link
Collaborator Author

dmcclean commented Sep 7, 2016

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
Copy link
Owner

bjornbm commented Sep 8, 2016

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

On 2016-09-07, at 16:09, Douglas McClean <[email protected] mailto:[email protected]> 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 #23 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AACRlPYC0Ql0eKsaqCeuHdZYhaQT8oRnks5qnsWHgaJpZM4BlU5z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants