
Andy Little <andy <at> servocomm.freeserve.co.uk> writes:
"Beth Jacobson" wrote
Andy Little wrote:
In PQS the units are part of the type. IOW the numeric value and its unit are always tightly coupled in code. That is a powerful feature. Once the numeric part of a quantity is disassociated from its unit then manual checking and external documentation is required, which is often the current situation wherever doubles are used to represent quantities. Manual checking doesnt always work as well as intended... That of course is why the Mars lander crashed!
Agreed! Any code containing a numeric literal for a physical quanity, or any code that inserts or extracts a numeric value from a quantity should be required to also specify the units of the numeric value. That's a critical part of the goal of the PQS library.
I agree that unit checking can be an extremely useful feature, I'm just not convinced that tight coupling is necessary to achieve that.
But I also agree here. The goal can be achieved without necessarily making the units part of the type. I'll explain.
There is planned another quantity where you can change the units at runtime FWIW.
I mentioned much earlier that my own implementation of a quantities library was more like the so-called "t2_quantity" - at least the way I envisioned the t2_quantity. But as I read the discussion it occurs to me that my idea may not be the same as yours, and it might be useful for me to share what I have in mind. I hope it will shed some more light on the "length::m vs. length" debate. A couple of people have suggested a syntax more like this: length d = 5 * meter; speed v = 2 * inch / second; time t = d / v; cout << "time = " << t / minute << "minutes"; I also like this usage - this is how my library works. As you've pointed out, Andy, this is trivial to achieve with the current PQS implementation, by using: typedef pqs::length::m length; typedef pqs::time::s time; typedef pqs::velocity::m_per_s speed; const length meter(1); const length inch(0.0254); const time second(1); const time minute(60); The problem is, it also allows sloppy code like this: length d(5); // no error; but units not clearly documented! speed v(2); // no error; but units not clearly documented! so that the numeric values are now divorced from their units (which are defined above, in the typedefs), thus potentially leading to another Mars lander problem. What I want is the syntax I illustrated but to enforce specifying units along with numeric values. It occurs to me that perhaps this would work: struct length : public pqs::length::m { length( pqs::length::m x ) : pqs::length::m( x ) { } operator=( pqs::length::m rhs ); } etc. I don't think this is a big departure from the current PQS concept. It's just a simple extension, really, but I think a very helpful one. As a user I'd rather have something like this provided for me than to have to write the extension myself for each type - length, time, speed, energy, etc. The cost is that I may have more units conversions happening behind the scenes. But they only occur when dealing with the numeric values themselves - i.e., input/output. All my internal computations should be occurring with my types length, time, speed, etc. - which would be defined using a common set of units. For many users a little conversion cost in I/O is of no consequence, if most of the time is spent doing calculations on values in length, speed, etc. (Other users can use the existing types that are tied to units.) One benefit is that I could write all my code to be independent of which units I choose, except where I need to specify a quantity numerically (in which case I must specify units, of course). If I later decide, say, the scale of my problem is more suited to eV than kJ (or if some of my code is reused in a differently-scaled application), I simply change the definitions for my "energy" class, etc., and recompile! As Beth Jacobson suggested, perhaps there could be an easy way for the user to select these "base units" for the entire application. To me, having types like "length," "speed," etc. is also easier to understand. It might be a better starting place for illustrating the library to new users. Here's an example of how I think about physical quantities: My desk has a length. "Length in meters" or "length in inches" are not properties of the desk - only length. That length can then be expressed numerically in different units. A "length" object would represent the desk's length in a similarly abstract way, which could then be expressed in whatever units are desired for I/O. Of course, internally the computer has to represent the length as a value in some unit, but that would be encapsulated in the type and thus transparent to the user. The same internal units would be used for all quantities, thus avoidinng the overhead of type conversions (except on I/O).
If my assumption that unit conversions are only needed when numeric values are assigned to or extracted from dimensions is correct, then dimensions don't really need to know about units. The units functions would need to have a concept of default dimension units so they'd know what they were converting from or to, but the dimensions themselves wouldn't know or care.
Well said. I think this is the same as what I'm suggesting.
That would be the real benefit of this system. Reducing the number of conversions and making units explicit at the point of conversion may have some small benefit, but making the dimensions library essentially unitless seems like a major advantage.
Of course it's your library, and I'm not trying to dictate design. But if you're looking for a way to make the dimensions library essentially independent of units while retaining the unit safety of the current system, this might be a direction to consider.
I agree. -- Leland