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

Angles as dimensions #79

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open

Angles as dimensions #79

wants to merge 19 commits into from

Conversation

dmcclean
Copy link
Collaborator

This is a sketch of the feature requested in #72.

It's offered for discussion purposes. Please do not merge without testing. It also requires better documentation before being merge-worthy.

@bjornbm
Copy link
Owner

bjornbm commented Jul 25, 2015

Thanks for your work here @dmcclean. I liked what I saw earlier but I'm super busy and on travel next week. I hope to take a closer look at this in early august.

@dmcclean
Copy link
Collaborator Author

Oh, no problem, take your time. I'm still not 100% convinced this is a good idea, but at least this gives something to play with to find out.

@bjornbm
Copy link
Owner

bjornbm commented Aug 12, 2015

I'm off for another two weeks of travel on Saturday and won't have a chance to look at this and named-units-2. One big blocking item is that I have a new computer and haven't even found the time to install GHC yet!

I'd be curious to know, when you have had time to play with either of these branches, what your experiences are, the impact to existing client code, etc.

@dmcclean
Copy link
Collaborator Author

Have fun! I'm traveling myself, but only as far as western Pennsylvania.

On angles

The answer is "sort of". I modified a branch of my autopilot project to use it. Most of the changes were eliminating uses of named-units-2 features since I haven't done the fairly significant merge work yet. There is basically no API difference, it's possible to use the non-angles branch in a way that converts directly to angles just by changing imports, if you have PlaneAngle synonyms for Dimensionless in the appropriate places, which I mostly did anyway for documentation.

It's satisfactory for me primarily because it verifies that I do the conversions properly, but I didn't discover any I had messed up. The only part I view with suspicion is the computation of torques from forces and distances, where the radian^-1 seems to come from thin air. On the other hand, having a Torque vs Energy distinction is great.

Could be interesting to have a dimensional-dk-experimental module for classical freshman physics and Euclidean geometry?

On named-units-2

In my opinion the named units feature is great and worth the wacky type signatures, which only appear in code that is polymorphic between variants. showIn is very nice. I have a type family for the value types of my signals that includes an associated type family that picks the "representation" info associated with signals of that type. So for quantities, the associated representation is a precision and a unit, and values get shown that way. For strings it's (), for integers it's a base, for booleans it's a (String, String) like ("On", "Off"), ("Open", "Closed"), etc. The result is that all signals get shown in a way that is both awesome and generic. A huge win over the log viewer from something like pixhawk, where everything is basically shown as a float, with some special handling that suppresses the fractional part when the value is an integer.

named-units-2 is the most comprehensive and best documented swing at it so far. The only part I'm not 100% sold on is the explicit tracking of Metricality in unit names. It primarily protects implementation correctness, not use site correctness. (Although it does also protect construction sites of new metric units.) Tracking of metricality for units themselves is on more solid ground as it protects primarily against use site correctness and not so much implementation correctness. The downside is implementation complexity primarily, but there is also a user impact in the form of weaken, which users need to use when they want to treat multiple metric and non-metric units uniformly, but not polymorphically.

An example of the latter problem, although this one is for unit names and not for units themselves. (The trouble here arises from the irregularity of using the kilogram as the base unit.)

baseUnitNames :: [UnitName 'NonMetric]
baseUnitNames = [weaken nMeter, nKilogram, weaken nSecond, weaken nAmpere, weaken nKelvin, weaken nMole, weaken nCandela]

I just globally searched my projects for an example, and the only one I found was this one (for unit names), and the one below for units themselves, neither of which is really user-facing because they are both in dimensional-dk itself.

-- | A 'Unit' whose 'Dimension' is only known dynamically.
data AnyUnit = AnyUnit Dimension' (UnitName 'NonMetric) ExactPi

instance Show AnyUnit where
  show (AnyUnit _ n e) = "1 " ++ (show n) ++ " =def= " ++ (show e) ++ " of the SI base unit"

instance HasDimension AnyUnit where
  dimension (AnyUnit d _ _) = d

-- | Converts a 'Unit' of statically known 'Dimension' into an 'AnyUnit'.
demoteUnit :: forall a d v.(KnownDimension d) => Unit a d v -> AnyUnit
demoteUnit u = AnyUnit dim (name $ weaken u) (exactValue u)
  where
    dim = dimension (Proxy :: Proxy d)

-- | Converts an 'AnyUnit' into a 'Unit' of statically known 'Dimension', or 'Nothing' if the dimension does not match.
--
-- The result is represented in 'ExactPi', conversion to other representations is possible using 'changeRepApproximate'.
promoteUnit :: forall d.(KnownDimension d) => AnyUnit -> Maybe (Unit 'NonMetric d ExactPi)
promoteUnit (AnyUnit dim n e) | dim == dim' = Just $ mkUnitR n e siUnit
                              | otherwise   = Nothing
  where
    dim' = dimension (Proxy :: Proxy d)

I believe that the reason this doesn't effect client code often or at all is a combination of the following factors:

  1. Most of it deals with quantities and not units
  2. When it does deal with units, most of it is dealing with a specific unit (which might be a complicated expression, but is an expression whose leaf nodes are specific units)
  3. When it does deal with an unknown unit, it is mostly with just one or two and they have independent type variables for the metricality, so they don't need to unify metric with non-metric units
  4. The operators for combining units, along with siUnit, eliminate basically all need for user code to interact with unit names themselves, especially in negative position. You describe what you want in terms of units and the names just kind of happen. (This just happening property is the reason why I think the feature is awesome, you don't have to think about it you just get free goodness.)

@dmcclean
Copy link
Collaborator Author

I made this comment on #46, but it applies in response to the explicitly-tracked 'Metricality' of 'UnitName's issue I discussed above, as it reduces the complexity somewhat and probably enough to make me lean in favor of keeping the tracking:

Made a change (ec98de7) to remove existentials from the UnitName GADT. I think there was a discussion around here somewhere [note: found it!] about whether tracking metric-ness of unit names was a step too far. I was leaning towards thinking it was, but actually most of the trickiness I was running into came from the existential types of the constructors and not from the phantom itself. Realized you don't need them because you can just use the Weaken constructor (through the weaken helper function) in a few more spots and voila they disappear.

@varosi
Copy link

varosi commented Jan 11, 2017

Hello!
Are there plans this branch to be merged into "master" in near future? It'll be very helpful to me.

@bjornbm
Copy link
Owner

bjornbm commented Jan 11, 2017

Hi @varosi,

I wouldn't hold my breath depending on your definition of "near" future. I'm sorry to say that we haven't done anything about on this branch in the last 1.5 years and it will no longer merge cleanly. That being said I have an upcoming project it may be quite useful so I hope to get some real world experience with it soon.

Depending on what you need we could consider other options such as releasing the functionality as a separate experimental package in the mean time. But with stack it is possible to depend on a specific git commit on github which may be sufficient for you? Perhaps you can clarify what is you main need?

@dmcclean: Do you have a feel for the amount of effort involved in bringing this branch in line with master? A diff show a ton of changes but perhaps a lot of it is straight-forward/mechanical?

@varosi
Copy link

varosi commented Jan 13, 2017 via email

@dmcclean
Copy link
Collaborator Author

I think it is all straightforward. Might or might not be easier to do by just redoing it on the new branch, I'm not sure, it depends how the auto-merge goes.

Has the decision been made that this is the direction we want to go? I think that has been the holdup, not doing the work. Although I don't have much spare bandwidth right now, new job and new son, but if this is desired I will get it released by the end of the month.

@bjornbm
Copy link
Owner

bjornbm commented Jan 14, 2017

@varosi: I think one of the sticking points is what it means exactly to have angles correctly handled. See #72 (in particular the quotes in this comment). The current implementation handles angles “correctly” from the point of view of the SI. But yes, it would be very nice if we could treat PlaneAngle as a separate dimension without too much cost in terms of inconvenience and/or verbosity. We just want to be sure we understand the cost before merging in master, which we can probably only be after putting it to substantial use.

@dmcclean: I don't think the decision has been made. I haven't had a chance to play with it at all. But if you feel confident that it is a clear win with limited drawbacks you can go ahead. Otherwise I propose that I gain some experience with it in my project this spring and then we make a call.

In the meantime it would of course be good, but perhaps not mandatory, if we could merge/port the changes to the current master. I will do this if/when I find time but you are of course also welcome to have a go at it. Let me know if you start!

@varosi
Copy link

varosi commented Jan 18, 2017 via email

@bjornbm
Copy link
Owner

bjornbm commented Jan 18, 2017

There is a usability cost. For example, should dividing two Lengths result in a Dimensionless or a PlaneAngle? If the Lengths are the distance along a circle arc and the radius of the circle then is should probably be a PlaneAngle, but if not it should probably be a Dimensionless. The Lengths themselves do not carry enough information to decide this so the user will have to be explicit about the expected type. In the SI where PlaneAngle and Dimensionless are equivalent this is not a problem in terms of the types (although still a problem for the user to make sure he does not use them in inappropriate ways).

The open question is if this usability cost (mainly by addingremoveAngles and coerceAngles functions if I recall correctly) is outweighed by the benefits compared to other approaches (for example newtyping, which will still be necessary if you want to distinguish between for example elevation and azimuth angles).

@varosi
Copy link

varosi commented Jan 19, 2017 via email

@varosi
Copy link

varosi commented Mar 2, 2018

Resurrection?

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 5, 2018

I would like to resurrect it. To me the critical issue is whether a Torque has a PlaneAngle in the denominator. I originally thought it did, but then I saw somewhat convincing arguments that it doesn't. If you can make a principled explanation of why it should be one way or the other, then resurrection is in play.

@bjornbm
Copy link
Owner

bjornbm commented Mar 6, 2018

I would also really like to see this resurrected as I have some good use cases to put it to the test (if I can find the time for them). I took a shot at aligning this branch with master a while back (last year?), but it wasn't as quick and easy as I had hoped (mostly due to not being sufficiently "fluent" in our current master) and I ran out of time.

Regarding Torque I think you (@dmcclean) are referring to an email conversion between the two of us during 2015-09-06/14. Let me know if you want me to forward it to you, or copy-paste relevant parts here. EDIT: I also find now some email discussion with regarding Brownstein's formulation.

@dmcclean
Copy link
Collaborator Author

dmcclean commented Mar 6, 2018

If you have time to paste the relevant parts that would be awesome.

@bjornbm
Copy link
Owner

bjornbm commented Mar 7, 2018

Here are the relevant parts of our conversation from 2015-09. I will forward the emails on the Brownstein paper (etc) to you as I don't want to paste the other correspondent's messages without his approval.

@dmcclean (2015-09-09):

Con: (and this is a big one) no matter what you do it breaks some equations from freshman physics. Both the r x F torque equation and the Erot = I w^2 equation, both of which pick up an extra rad^-1. The second one is more problematic since it breaks symmetry with the equation for the translational case. You have to lose symmetry for either the momentum equation or the energy equation, and it makes more sense to break it in the energy equation because the other choice has bad knock-on effects. As far as I know there is no way out of this.

@bjornbm:

Regarding the problems you highlight with torque and rotational energy, they seem to be resolved by substituting (r / 1 rad) in place of r in the equation for torque and moment of inertia. With this substitution deriving one from the other via angular acceleration or angular momentum leads to consistent dimensions, and Erot turns out correct too. It also “fixes” the below peculiarity (from https://en.wikipedia.org/wiki/Torque#Units):

pastedgraphic-2

Can I justify the substitution? Not in any rigorous sense, although it does somehow make the radius meaningful in an angular sense (“radius of gyration”?). Does it cause other problems? Don’t know, perhaps you have been down this track already?

@dmcclean:

Starting with the angles thing. I think you are right. What had thrown me off was that I had written the angles branch with:

type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero

But that isn't what you get if you do it correctly. It should be type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero 'Neg1 'Zero. Then the angular momentum = moment of inertia * angular velocity equation works out, the rotational kinetic energy equation works out, and I am back to thinking the whole thing is on balance a good idea. As you say, some of the changes are hard to motivate, so it probably needs a writeup "how basic newtonian mechanics works in this model", but it does lead to better self-documenting code and hopefully fewer mistakes.

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

Successfully merging this pull request may close these issues.

None yet

3 participants