The Units Pipe Dream

## The Units Pipe Dream

### Midnight to Three...

David J. Anderson
March 2008

Numbers are everywhere. They surround us in every direction we turn - the price of gas, the distance to the beach, the temperature outside, the weight we want to lose. Sometimes we mention the units - \$3.29/gallon, 46 miles, 75 F, 15 lbs - but often they're understood intuitively, as if they were part of the local dialect. Though we may mention them in conversation, there's little penalty for leaving them unspoken: "three twenty-nine", "forty-six", "seventy-five", and "fifteen" are perfectly good conversational numbers. Because we're embedded in society and share common contexts, being explicit about units isn't strictly necessary.

It's only this common context that allows us to consider specifying units a formality. Things only get confusing when we switch contexts and converting between unit systems is necessary - 1.02 euros/liter, 74 kilometers, 24 C, 7 kgs. We stumble and fumble as we do our best to quickly compute estimates for these values in our heads until we've gotten a feel for the new units. When unit-intuition is lost, numbers are meaningless to us.

Whether or not they're mentioned, units always accompany numbers in the real world. Numbers by themselves are just a reckoning system - it's only when they're attached to units in context that they have meaning. Though mathematics has abstracted the concept of number and allowed us to count independently of units, the units aren't unimportant; they're still there lurking behing the scenes.

Programmers must work between the two perspectives depending on the kind of programs they're writing - either treating numbers as unitless, or being forced to consider units more formally. Because they aren't supported directly, in most situations units are an afterthought. Numeric values are calculated by programs and output onto screens, plugged into reports and spreadsheets - units are simply hardcoded on the screens and forms. When units are considered to be implicit in the problem domain, numbers can just be treated as integers, floats and doubles without unit. So units typically aren't carried through computations. They are a pain to deal with and they just slow programmers down, getting in their way for no good reason. Most programs today count and calculate without a real concern for units.

A cavalier attitude like this can cause serious problems, however. Incorrect conversion factors and forgotten scalings have led to the ruin of many projects - bridges have fallen down, planes have flipped over, buildings have toppled, boats have sunk. There needs to be some way to avoid this. It should be easy to associate units with numbers and convert them reliably within and between different systems of measurement. It's not a new idea, and many valiant attempts have been made.

### Ruby and Units

The Ruby programming language does not have explicit support for units. Ruby developers will typically treat units as an afterthought in the same way as they would in other programming languages, converting unit-less numbers using conversion factors and simple transformations. However, there are a few frameworks for units that have been developed.

The three prevalent systems that are used to convert units are the hard-coded unit converters in Rails, the Units portion of the Facets Rubygem, and Carlson and Buttler's units Rubygem.

#### Unit Conversions in Rails

Rails ActiveSupport provides a simple unit conversion paradigm for several unit conversions, allowing you to say things like:

 3.weeks + 2.days ==> 1382400 (seconds) 20.minutes.ago ==> 1176491007 (seconds since the Epoch)

However, this is driven by hard code, not conversion data. And as such, it can fail if incorrectly:

 1.minute ==> 60 (seconds) 1.hour ==> 3600 (seconds) 1.hour.minutes ==> 216000 (wait - I expected 60...)

Rails gives you a handy way to do conversions, but they have to be simple and there's nothing but numbers supporting them from underneath - they're not complete and aren't easily extensible. They do many of the things web developers need, though, and they come for free in today's most popular Ruby-based web development framework.

#### The Facets Rubygem

Since 2003, the Ruby Facets library has been growing, filled with new classes and extensions to existing classes. It's a truly phenomenal piece of work.

Part of this package is a units system that is quite full-featured.

 1.minute ==> 1 (minutes) 1.hour ==> 1 (hour) 1.hours.to(minute) ==> 60 (minutes)

A complete SI system is even available. The system promotes units as an object, and provides arithemtic methods to handle unit type compatability and conversion. The data files are formatted in YAML, and are extensive. However, it's missing a tie between the numbers and the units - the two are still separated conceptually.

#### The units Rubygem

In 2005, Carlson and Butler put together units.gem, which started down a different path, also loading their conversions from data and using method_missing to do conversions:

 1.minute ==> 1 (minutes) 1.hour ==> 1 (hour) 1.hours.to_minutes ==> 60 (minutes)

Very good stuff. But their definition strategy was not self referent, as the Facets system is; and their implementation did not promote units to first class objects.

#### It's All Good, but...

I also wanted to say some things like:

 Units.length.systems ==> [ 'english', 'metric' ] Units.second.unit_type ==> 'time' a = Time.minutes_per_day ==> 1440 (minutes) a.to_s ==> '1440 minutes' a.hr.to_s ==> '24 hours'

Basically units are a Domain Specific Language. They define things relative to each other, and the meaning of unit expressions is intuitive and flexible based on the types of the arguments. I wanted to define the units much like the way database migrations are defined in Rails, and allow forward reference to take place during definition, coding units and conversions in Ruby. I wanted the code readability to be at the same level as Ruby, using the method_missing to decompose messages and do the work, and optionally be able to reliably create methods - not have everything be pre-created. I also didn't want the package to be a heavyweight. I wanted it lean and mean and fast.

I felt that something comprehensive could be done and packed into a Rubygem that would give me a nice extensible framework for units in Ruby. So I started on a programming expedition...

### The Pipe Dream - Part 1 - The Philosophy of Units

I began with some pondering. What are units, really? And how are they organized so that we can assume them without thinking about them?

Units are the quality of quantities that provide the context in which they can be compared to each other. This comparison is done in a measurement context - only values in the same context can be compared, such as by length, weight, or duration. A measurement context is expressed systematically, that is in the context of one or more system of units. Finally, the unit values themselves are defined within a system context.

Context is everything when it comes to our fundamental understanding of units. This taxonomy of Measures / Systems / Units provides what we need to put everything in perspective. For instance, we probably include the following subset:

 Measure System Unit Length English Yard Foot Inch Metric Meter Centimeter Mass English Pound Ounce Metric Kilogram Gram Time Base Hour Minute Second

We can only compare values with the same Measure, we can convert values between Systems, and Units within those Systems can be sorted by scale. Contextually, knowing a Unit (either explicitly or implicitly) also means you know its Measure and System. It now also makes sense to convert between Units and Systems.

Interestingly, notice that we could have rooted the taxonomy with Systems instead of Measures. I chose Measure as more fundamental since it has its roots in discovery rather than invention, and Measure bounds the conversions we can do at the broadest level. In the chart, though Systems may occur in different Measures, they may not exist in all Measures which would leave us with gaps. The organization in the chart is not without it's own gaps (Time is labeled as Base, for example) but at least there are not gaps at the top level. And besides this, there is also a sense of cause and effect to this hierarchy: Measure came first by observing of the world around us, while systems were invented subsequent to the need to measure.

Also, we may have different names for Measures that mean the same thing formally, but have different informal contexts. For instance, Length and Distance are formally equivalent, but Length has a static connotation while Distance connotes movement. Time and Duration have are also equivalent but have different connotations (again, interestingly, static and dynamic.) The simple rule is that if you can use the same Units, the formal equivalence holds even though we may talk about the Measures differently.

Setting Ruby code to this, we have:

 units.rb   a units pipe dream ``` class Units @@measures = {} end class UnitsMeasure < MethodicHash end class UnitsSystem < MethodicHash end class UnitsUnit < MethodicHash end ```

The `Units` class is at the top level to provide a global context for defining and accessing the `Units` system, and managing a `Hash` of `UnitsMeasures.` `UnitMeasures` is a `MethodicHash` that contains `UnitsSystems`, and `UnitSystems` is a `MethodicHash` that contains `UnitsUnits`. A `UnitsUnit` is a `MethodicHash` that contains its properties.

Note the use of the `MethodicHash`. This class is just a `Hash` that defines its `method_missing` method to use its method argument as the value of the key used to store or retrieve its values. It's a convenient shorthand that makes accessing the hash look like accessing instance variables. The mechanism will be replicated at the class level in the `Units` class because it is not used to create instances, but only to root the sofware.

So, let's take the next step and detail the code skeleton.

#### The Units Skeleton

`Units` is used at the class level to manage a `Hash` of `UnitsMeasure` instances, so all of the methods are class methods.

 units.rb   a units pipe dream ```class Units def Units.units_measures @@measures.keys end def Units.create(name, &block) measure = (@@measures[name.to_s] ||= UnitsMeasure.new) block.call measure if block_given? measure end def Units.derive(name, target, &block) measure = (@@measures[name.to_s] = (target.kind_of? UnitsMeasure) ? target : Units.create(target.to_s)) block.call measure if block_given? measure end def Units.delete(name) @@measures.delete name.to_s end def Units.clear @@measures.clear self end def Units.size @@measures.size end def Units.[](name) @@measures[name.to_s] end def Units.method_missing(method,*args) measure = self[method] raise UnitsException.new("UnitsMeasure '#{method}' undefined") if !measure measure end def Units.names_of(units_measure) @@measures.keys.select { |name| @@measures[name].equal? units_measure } end end ```

The methods are all fairly simple and provide guarded access to the `Hash`. The `units_measures` method returns an `Array` of the names of the currently defined `UnitsMeasure`s. The `create` method returns a new `UnitsMeasure` with the given, or the existing one with the given name if present. If a block is passed in, the `UnitsMeasure` is yielded to it. The `derive` method is much like `create`, associating a `UnitsMeasure` to the given name, except it is derived from existing `UnitMeasures`. If the target of the derivation is a `UnitsMeasure`, the name is associated with it. Otherwise a new `UnitMeasure` is created with the value of target as its name, and the name is associated with it. If a block is passed in, the `UnitsMeasure` is yielded to it. The `delete` method disassociates the name with a `UnitsMeasure,` and returns the name. The `clear` method empties the `Hash` of `UnitMeasure`s and returns `Units`. The `size` method returns the number of `UnitMeasure`s in the `Hash`. The `[]` method returns the `UnitsMeasure` with the given name from the `Hash`. The `method_missing` method tries to get the `UnitsMeasure` using the name of the method as the associated name, and returns it. If no value was found, a `UnitsException` is raised. The `names_of` method returns an `Array` of names that are associated with the given `UnitsMeasure`.

The key feature of the class is that it allows a `UnitsMeasure` to have multiple names associated with it, preserving formal equality while admitting connotative difference.

#### The UnitsMeasure Skeleton

A `UnitsMeasure` instance associates names to `UnitsSystem`s.

 units.rb   a units pipe dream ```class UnitsMeasure def system(name,&block) system = (self[name] ||= UnitsSystem.new(self,name)) block.call system if block_given? system end def names Units.names_of self end end ```

There's not a lot to `UnitsMeasure` at this level, since most of the mechanism is managed by the `MethodicHash` from which it inherits. The `system` method creates a new `UnitsSystem` in the context of this `UnitsMeasure` or gets the existing one if found, and yields it to the block if it was passed in. The `UnitsSystem` is returned. The `names` method returns the names by which this `UnitsMeasure` is known at the top level.

#### The UnitsSystem Skeleton

A `UnitsSystem` instance associates names to `UnitsUnit`s.

 units.rb   a units pipe dream ```class UnitsSystem attr_reader :units_measure, :name def initialize(units_measure,name) @units_measure = units_measure @name = name end def unit(attributes) unit = UnitsUnit.new self, attributes self[unit.name] = unit end end ```

There's not a lot to the `UnitsSystem` class either, since most of the mechanism is managed by the `MethodicHash` from which it inherits. However, it does know to which `UnitsMeasure` it belongs. The `initialize` method sets up a new `UnitsSystem`, remembering its name and the given `UnitsMeasure`. The `UnitsSystem` is returned. The `unit` method associates a new `UnitsUnit` in the `UnitsSystem` with its name from the given `Hash` of options. The `UnitsUnit` is returned.

#### The UnitsUnit Skeleton

A `UnitsUnit` is a unit in a `UnitsSystem`, defined by its attributes.

 units.rb   a units pipe dream ```class UnitsUnit attr_reader :units_system def initialize(units_system,attributes) @units_system = units_system merge! attributes end end ```

The `UnitsUnit` is trivial, containing only a simple initializer. The `initialize` method remembering the given `UnitsSystem` and merging the attributes into itself. The `UnitsUnit` is returned.

#### The UnitsException

Of course, when unexpected things happen related to `Units`, a `UnitsException` is raised.

 units.rb   a units pipe dream ```class UnitsException < Exception end ```

This is just a cover for `Exception` in the `Units` context.

At this skeletal level, the classes are simple. The fun begins when they are fleshed out. Our next step is taking a closer look at Measures.

### The Pipe Dream - Part 2 - Derived Measures

Measures come in two flavors: those that exist independently and stand up on their own, and those that are derived from other Measures. For instance, if length is an independent Measure, then area can be derived as length*length.

Since when you're working with measures the only thing that makes sense is multiplying them together, you only need to maintain three operations: multiplication, division and exponentiation. When units are derived, a combination of other units are combined to produce a new type of unit. For instance, consider kilograms divided by cubic meters. This formulation yields a density in kg/m3. But from the Measures point of view, you aren't interested in the kilograms and meters - it's mass and volume that are formulated to get density. The Density Measure is what is derived and is what drives the specific unit conversions.

Way back in an early mathematics class I was taught the concept of Dimension Analysis using what they called the fencepost method, used to figure out the units when factors are multiplied. The image is this: you have a series of fenceposts with a single rail between them. You line up the unit "dimensions" of a each value between the fenceposts such that the unit numerators are above the rail and the denomenators are below the rail. Then you run the rails, cancelling out any units that are both above and below. What's left are the units of the result. At the time it was a revelation, but of course it was only a simple crutch until a little later when negative exponents were introduced and it could all be done by addition and subtraction of exponents.

#### Measures Arithmetic

Only a few methods are needed to facilitate deriving Measures.

 units.rb   a units pipe dream ```class UnitsMeasure attr_reader :derived def **(exponent) UnitsMeasure.new.merge_derivation(self => exponent) end def /(divisor) UnitsMeasure.new.merge_derivation(self).merge_derivation(divisor,-1) end def *(factor) UnitsMeasure.new.merge_derivation(self).merge_derivation(factor) end def merge_derivation derivation, multiplier=1 current = (self.derived ||= {}) if derivation.kind_of? UnitsMeasure if derivation.derived derivation.derived.each {|key,value| current[key] = (current[key] || 0) + multiplier*value } else current[derivation] = (current[derivation] || 0) + multiplier end elsif derivation.kind_of? Hash derivation.each {|key,value| current[key] = ((current[key] || 0 ) + multiplier*value) } else raise UnitsException.new( "Cannot add #{derivation.self_name} to derivation") end self end end ```

The Measures derivation is simple when viewed this way. The `**` method creates a new `UnitsMeasure` and merges itself into the derivation raised to the given power. The `/` method creates a new `UnitsMeasure`, merges itself to the derivation, and merges the divisior to the derivation with its exponents inverted. The `*` method creates a new `UnitsMeasure` and merges itself and the factor to the derivation. For each method, a new `UnitsMeasure` is returned.

The `merge_derivation` method is the real workhorse. If the given derivation is a derived `UnitsMeasure` or a `Hash`, each element of the derivation is merged. If the given derivation is an underived `UnitsMeasure`, it is merged. Otherwise, we can't deal with it and a `UnitsException` is thrown.

The derivation is a `Hash` that is kept in the `derived` instance variable in the `UnitsMeasure` that associates `UnitsMeasure`s to exponents. When the derivation is merged into a `UnitsMeasure` using `merge_derivation`, the exponents of existing elements are modified, or new elements are added if missing. What results is a new `UnitsMeasure` with the value of its derived set to the Measures that define it.

#### Attaching Derived Measures

Given the ability to create derived Measures, we must still be able to add them to the set of measures in the `Units` class.

This brings up another twist, which we handle in the same way we did for the `UnitsMeasure` creation. Namely, if two derived `UnitsMeasure`s have the same derivation, we consider them to be formally equivalent. This makes sense: we consider all Measures to be unified. It goes right back to the previous statements about formalism and connotation. Two derived Measures can be formally equilvalent, but called by different names to preserve difference in connotation.

 units.rb   a units pipe dream ```class Units def Units.derive(name, target, &block) measure = ( @@measures[name.to_s] = if target.kind_of? UnitsMeasure if target.derived && (derived = find_by_derivation target.derived) derived else target end else Units.create(target.to_s) end ) block.call(measure) if block_given? measure end def Units.find_by_derivation(derivation) matches = @@measures.values.uniq.select { |measure| measure.derived ? measure == derivation : false } case matches.size when 0 then nil when 1 then matches else raise UnitsException.new( "Multiple UnitsMeasures with same derivation found") end end end ```

The `derive` method is still the analogue of the `create` method, but it's been beefed up to handle derived `UnitsMeasure`s. Given a name and target, if the target is a `UnitsMeasure` it checks to see if it is derived. If so, then it so then it sets it to a known `UnitsMeasure` with the same derivation, or adds it anew. If it isn't a `UnitsMeasure`, then name is a new connotation of the target, and the name is associated with the target `UnitsMeasure`. Note that `create` is called with target - which either finds its `UnitsMeasure` or creates a new one. Finally, the resultant `UnitsMeasure` is yielded to the block if it was given, and returned to the caller.

The `find_by_derivation` method finds a known `UnitsMeasure` matching the given derivation if it exists. Each of the known derived `UnitsMeasure`s' derivations is tested for equality with the given derivation. If there are no matches, `nil` is returned. If one was matched, the match is returned. If more than one was found, then a unification problem occurred and it is time for us to raise a `UnitsException`.

We'll use the `UnitsMeasure`s to do our conversions later. But before that, we need to detail the definition of Unit values.

### The Pipe Dream - Part 3 - Unit Values

As people establish what sorts of things are going to be measured, Unit values are designated so that comparisons could be made. Whether it was sticks laid end to end measuring distance, rocks used in a balance to measure weight, or the position of the sun and moon in the sky to measure time, unit values provided a new sense of order to the world. We all could finally relate different objects to each other with common scales.

In human language there are three common constructs for speaking of units - by name, by the plural of the name, and by abbreviation. Since most of us capture the subtleties of this before we're grown, we hardly pay attention unless we encounter ambiguity or something we've not yet experienced. Fortunately for us, unit names, plurals and abbreviations are common and we have no problem with them.

The same cannot be said of computer software, of course. Computers have no real world experience to draw upon. We'll need to explicitly state the names, plurals and abbreviations we use to reference the units we define.

 units.rb   a units pipe dream ```class UnitsUnit def initialize(units_system,attributes) @units_system = units_system merge! attributes normalize end def normalize raise UnitsException.new("UnitUnits must have a name attribute") unless self.name self.name = self.name.to_s add_plural add_abbrevs self end def add_plural self.plural = (plural = self.plural) ? plural.to_s : self.name+'s' end def add_abbrevs(attribute = nil) if attribute == nil self.abbrevs = add_abbrevs(:abbrev)+add_abbrevs(:abbrevs) elsif (value = self[attribute]) self.remove(attribute) (value.kind_of? Array) ? value.collect {|e| e.to_s } : [ value.to_s ] else [ ] end end end ```

When a `UnitsUnit` is created, a short dance is done to normalize its name, plural and abbreviations. The `initialize` method is extended to call the `normalize` method after the incoming attributes have been merged into the instance and the instance is returned. The `normalize` method makes sure the name of the instance is a `String` and adds plurals and abbreviations to the instance. If no name is given as an attribute, a `UnitsException` is raised. The instance is returned. The `add_plural` method asigns the `plural` attribute and makes sure that it is a `String` if it exists. If it doesn't exist, it is created by adding an 's' to the value of the name.

The `add_abbrevs` method make sure that the `abbrevs` attribute is an `Array` of `Strings`. For flexibility, the values may be passed using the `abbrev` or `abbrevs` attribute, either as an `Array` or a single abbreviation. The method is intersting in that on entry from `normalize,` no arguments are given and the attribute is `nil`. This causes the `abbrevs` attribute to be set to the sum of the value of `add_abbrevs` called with the `abbrev` and the `abbrevs` attributes. When the method is entered with a non-`nil` attribute, the value is remembered, the attribute is removed and an `Array` is returned. If the value was an `Array,` it's elements are converted to `Strings`; if not an `Array,` then an `Array` is constructed from the value converted to a `String`. Finally if the value is `nil`, the attribute not being present, an empty `Array` is returned. And now, going back to the beginning, we can see the sum is a sum of `Arrays`, resulting in a new `Array` stored into the `abbrevs` attribute.

So now we have nice little mechanism that sets up named unit values with plurals and abbreviations. But to get at them, we need to build a quick-access mechanism.

 units.rb   a units pipe dream ```class UnitsSystem def unit(attributes) unit = UnitsUnit.new self, attributes self[unit.name] = unit Units.add_unit unit end end class Units @@unit_names = {} @@unit_plurals = {} @@unit_abbrevs = {} def Units.add_unit(unit) @@unit_names[unit.name] = unit @@unit_plurals[unit.plural] = unit unit.abbrevs.each { |abbrev| @@unit_abbrevs[abbrev] = unit } end def Units.clear @@measures.clear @@unit_names.clear @@unit_plurals.clear @@unit_abbrevs.clear self end end ```

We create the high speed mechanism by rooting it at the top level, through `Hash`es of the unit's names, plurals and abbreviations. In `UnitsSystem`s, the `unit` method is expanded to make a call to the `add_unit` method in `Unit`s. The `Units.add_unit` method associates the unit's name, unit's plural and each of the unit's abbreviations to the unit. The `Units.clear` method is extended to do a last spot of bookkeeping, emptying the three new `Hash`es.

Since it can look upwards for its `UnitsSystem` and `UnitsMeasure`, all of the relevant parts of the `Units` infrastructre can be retrieved once a `UnitsUnit` has been identified. Using these three hashes, identifying a `UnitsUnit` should very fast.

But what using these structures? There still isn't a way to equate units and look them up in context. That just happens to be our next step. But take care. It turns out this one is a doozy!

### The Pipe Dream - Part 4 - Equating Units

When the relationship between units is being defined, we need to effectively say things like:

1.foot = 12.inches
1.hour = 60.minutes
1.ounce = (1/16.0).pound

In the scheme we've set up so far, we make our unit definitions in code that reads a lot like the Rails DB migrations. We'll go ahead and add an `equals` attribute to our incoming unit `Hash` handling:

 definitions.rb   a units pipe dream ``` Units.create :length do |m| m.system :english do |s| s.unit :name => :inch, :plural => :inches, :abbrev => :in s.unit :name => :foot, :plural => :feet, :abbrev => :ft, :equals => 12.inches s.unit :name => :yard, :abbrev => :yd, :equals => 3.feet end end ```

Now we've done it - the can of worms is opened. Up to this point we've just been building interconnected data structures. But with this simple inclusion, we have a whole set of additional issues to contend with. How the heck can we make this work?

Let's consider the problem in the context of this simple inch-foot-yard example. The first thing to think about is that the inch unit has no equals attribute. Therefore, we'll consider it to be the basis for the Measure - all un-equaled units will be bases, and other Unit values will be relative to the bases. The next thing to notice is that the unit equality does not need to be defined in terms of the base. This is important since, for instance, people may define a mile as 5280 feet, but it is not likely they'll define it as 63360 inches. That is a constraint to which we do not want to be subject - we shouldn't have to define equality in terms of a base; computers should be figuring out all that for us. Of course, people may units in whatever way they wish - we don't want to preclude anyone from doing things in any way that makes sense to them.

So given this, how should we record the equality?

We'll focus on the `equals` attribute of the foot unit first. As given, the statement `12.inches` will trigger a method_missing on the `Fixnum` object. Great - we'll just add a method_missing on `Fixnum`, do a conversion, attach the `UnitsUnit`, and away we go... Except, the `Fixnum` is a singleton... shared across all usage. And we want the same activity for other types of numbers. No, just adding units to a `Numeric` isn't quite good enough.

So we'll still put a `method_missing` on `Numeric`, but instead of converting the value and adding a `UnitsUnit` just, we'll create and return an instance of a new object, `NumericWithUnits`. Besides the advantage of not crufting up `Numeric`, we'll be able to add other methods to the new object that can handle conversions leaving us open to do all the crazy things we can dream up.

One might wonder why we're stoping with `Numeric`s. Why not an `ObjectWithUnits`? This is a good question and we'll address it shortly. For now though, let's take a look at the consequences of our design choice and see how things shake out.

 units.rb   a units pipe dream ```class NumericWithUnits attr_reader :numeric, :unit def initialize(numeric,unit) @numeric, @unit = numeric, unit end def to_s "#{numeric} #{(numeric == 1)? unit.name : unit.plural}" end end class Units def Units.lookup(unit_identifier) unit_identifier = unit_identifier.to_s @@unit_names[unit_identifier] || @@unit_plurals[unit_identifier] || @@unit_abbrevs[unit_identifier] || nil end def Units.convert(numeric,unit_identifier) unit = lookup(unit_identifier) raise UnitException.new("#{unit_identifier} missing") unless unit NumericWithUnits.new(numeric*unit.equals.numeric,unit.equals.unit) end end class UnitsUnit def normalize raise UnitsException.new("UnitUnits must have a name attribute") unless self.name self.name = self.name.to_s add_plural add_abbrevs add_equals self end def add_equals self.equals = NumericWithUnits.new(1,self) unless self.equals end end class Numeric alias :method_missing :method_missing_before_units def method_missing(method,*args) begin unit = Units.convert self, method rescue method_missing_before_units(method,args) end end end ```

The changes required are not too complicated. The `NumericWithUnits`' `initialize` method assigns the incoming `Numeric` and `UnitsUnit`. The `NumericWithUnits`' `to_s` method returns a `String` containing its numeric part followed by its unit name or plural. The `Units.lookup` method returns the unit associated with a name, plural or abbreviation if it exists, or `nil` if it can't be found. The `Units.convert` method takes a numeric value and a unit name, plural or abbreviation and returns a new `NumericWithUnits` instance. The numeric part of the instance is converted relative to the identified unit, and the identified unit is assigned to the instance. If the name, plural or abbreviation could not be found, a `UnitsException` is raised. The `UnitsUnit`'s normalize method is extended to call the `add_equals` method to add its `equals` attribute if missing. The `UnitsUnit`'s `add_equals` method makes this unit a base - that is, if it has no `equals` attribute, it makes it a new `NumericWithUnits` instance with a quantity of 1 and a unit of itself. Finally, the `Numeric`'s `method_missing` method is extended to try to convert to the given units if possible, or otherwise call the prior `method_missing`.

Great! Now we can load in units and have them all defined in our data structures in terms of base units. With these equalities in place, conversion is trivial!

#### But What About Context?

The `Units.convert` method does conversion quite nicely - but thinking ahead, we only want it to convert to the base units during definition! When we're not defining the units, we want to keep the units that are given:

 5.feet ==> NumericWithUnits: numeric = 5, unit = feet 1.5.hours ==> NumericWithUnits: numeric = 1.5, unit = hour 1.ounce ==> NumericWithUnits: numeric = 1, unit = ounce

What we need is a switch that only does the conversion based on whether or not we're in a defining context. We'll take that to mean: when calling the `system` method of a `UnitsMeasure`. Inside of these calls the system is in a defining context... but outside, is a using context. Note that the unit method of a `UnitSystem` is also a defining context, but units are not ever accessed within it except for in a defining context - nothing special is needed.

 units.rb   a units pipe dream ```class Units @@defining = nil def Units.defining? @@defining end def Units.defining(defining) @@defining = defining end def Units.convert(numeric,unit_identifier) unit = lookup(unit_identifier) raise UnitException.new("#{unit_identifier} missing") unless unit if defining? NumericWithUnits.new(numeric*unit.equals.numeric,unit.equals.unit) else NumericWithUnits.new(numeric,unit) end end end class UnitsMeasure def system(name,&block) Units.defining true system = (self[name] ||= UnitsSystem.new(self,name)) block.call system if block_given? Units.defining nil system end end ```

Definition declaration sort of crosscuts through this stuff; besides the creation of a `defining` class variable to hold the definition state in Units, additional changes must be made. The `Units.defining?` method returns `true` if a measure is being defined, indicating the defining context. The `Units.defining` method sets the measure being defined. The `Units.convert` method is extended to only convert to the base when in the defining context. Otherwise the initialization of the `NumericWithUnits` is done directly with the numeric and the unit that was found. The `UnitsMeasure`'s `system` method is extended to indicate the defining context by setting and clearing itself.

By regulating all of the pathways into the `system` method, we ensure that the system will continue to work reasonably - below `system`, there's no place to establish a contextual foothold. If control goes through the method, we'll certainly get the context right unless another developer down the road starts changing our code. But since we're also writing unit tests as we go along here, I'm not too worred.

But we're not done yet.

#### What About Ambiguity?

In the last example, we used the unit "ounce". But did we mean a measure of volume, or were we perhaps really talking about weight? We don't know! It's ambiguous!

There are two ways to circumvent ambiguity The first is by avoiding it: simply work in a perfect world where it never appears. The second is to deal with it. Since we live in the real world, we must use or extend the context we've established to try to figure out something reasonable. But it may get a little hairy. Context is like that.

What ambiguity implies is choice. If there's just one thing, there's no ambiguity - it's only when more that one thing that can be chosen that ambiguity becomes an issue. When we're choosing from unit names, or plurals, or abbreviations, we've been expecting to see that we'll only get one unit back. But when we lookup ounces, we would potentially get more than one unit. We need to change out code a bit to turn this potential into reality.

This actually sheds some light on the the entire lookup process. Why do we need three `Hash`es when one would do? At the time, it seemed like a good idea to keep the name, plural and abbreviation different `Hash`es; but since we're just going to use them for lookup at the same time, let's reduce the code a bit while we're adjusting things.

Additionally, we need to take a closer look the defining state. While setting a `true`/`false` flag only conveys whether we're defining or not, we're going to need some more information to get us a further. We'll make a slight change to the mechanism to record the measure being defined - that way we can compare the unit being defined against it. If we encounter two units with the same name when defining, we'll prefer the one with the measure that matches the one we're defining. In fact, we'll also look at the derivation and if the requested unit is in a measure that's part of a derivation, we'll choose it over one that's not.

 units.rb   a units pipe dream ```class UnitsMeasure def system(name,&block) Units.defining self system = (self[name] ||= UnitsSystem.new(self,name)) block.call system if block_given? Units.defining nil system end end class Units def Units.add_unit(unit,unit_identifier=nil) if unit_identifier if (element = @@units[unit_identifier]) @@units[unit_identifier] += [ unit ] unless element.index(unit) else @@units[unit_identifier] = [ unit ] end else add_unit unit, unit.name add_unit unit, unit.plural unit.abbrevs.each { |abbrev| add_unit unit, abbrev } end end def Units.lookup(unit_identifier) @@units[unit_identifier.to_s] || [ ] end def Units.convert(numeric,unit_identifier) if (candidates = lookup(unit_identifier)).size == 0 raise UnitsException.new("#{unit_identifier} missing") elsif !defining? if candidates.size > 1 raise UnitsException.new("#{unit_identifier} ambiguous") else unit = candidates NumericWithUnits.new(numeric,unit) end else units = candidates.select { |candidate| @@defining == candidate.units_system.units_measure } case units.size when 0 then raise UnitsException.new("#{unit_identifier} missing") when 1 then unit = units NumericWithUnits.new(numeric*unit.equals.numeric,unit.equals.unit) else raise UnitsException.new("#{unit_identifier} ambiguous") end end end def Units.clear @@measures.clear @@units.clear self end end class UnitsUnit def ==(unit) self.equal?(unit) && self.units_system.equal?(unit.units_system) end end class NumericWithUnits def ==(value) (value.numeric == numeric) && (value.unit.equal? unit) end end ```

The change to the indicate the defining state is slight. The change to handle the conversion in the defining versus using state is much more dramatic. The `UnitMeasure`'s system method now sets the defining state to the measure of the unit being defined rather than just a true value. The `Units.add_unit` method is modified to add associations from the unit's name, plural and each abbreviation to the unit - but the associations are now to `Array`s of units. Initially, we don't supply a `unit_identifier`. This triggers the code that adds the name, then the plural, then each abbreviation by re-calling the method. This time around a `unit_identifier` is supplied which adds the association to the `Hash` if not already present. We need this test-of-presence because if we redefine a unit, we need to make sure it only gets into the `Array` once.

The `Units.convert` method is significantly changed. First, if there are no units defined that match the `unit_identifier`, an exception is raised. If there are units, then if the system is in the defining state, then if a single unit matches, we return a new `NumericWithUnits` - otherwise, we have more than one matching unit and we have ambiguity. We'll figure out how to deal with using state ambiguity later; for now we'll just raise a `UnitsException`. If we're in the defining state, then we make sure the unit being used in the definition is in the right context. Once we've eliminated the units that don't match this tests, we should have gotten rid of our potential ambiguity. A single match indicates a non-ambiguous choice and we create a new `NumericWithUnits`; zero matches indicates a missing unit and more than one match means we still have ambiguity, so we throw a `UnitException`. The `Units.clear` now just clears out the one `Hash` that has replaced the three.

The `UnitsUnit`'s `==` method is replaced and tests that the underlying `MethodicHash` is the same given unit. This is done so the index call on the `Array` in the `Units.add_unit` method will add the unit if it's in a different measure/system context - otherwise, we wouldn't add units with the same attributes. We also have to define `NumericWithUnit`'s `==` method: equivalent numeric, the same unit.

Note that we do not go down to the system level for our ambiguity checks. Why not? Because we're just not going to find two different units in the same measure context with the same identifier. That would just be too unreasonable - while we aren't writing code for the perfect world, we have to assume everything is at least reasonable.

#### And What About Those Pesky Raises?

There are two types of `UnitsException`s we generated to deal with ambiguity: one for a missing unit - not found in the lookup, and the other for multiple units - too many found. While schematic problems could cause both errors, the first could also be a forward referencing issue - occuring when we are referencing a unit ahead of its definition. Happily, this problem can be overcome to a large extent by embedding a forward reference solution into the system.

The first thing we need to do is sort out our `UnitsExceptions`:

 units.rb   a units pipe dream ```class MissingUnitsException < UnitsException end class AmbiguousUnitsException < UnitsException end ```

When we're defining units, if we get a `MissingUnitsException` we want to remember it and revisit it later when more information is present in the system - say, when more units have been defined. The rationale is this: if unit X is defined in terms of unit Y, but unit Y is encountered after unit X, then unit X will be missing unit Y and an exception will be raised. So, we remember the exception, keep accepting units, and once more units have been defined (possibly containing the definition of unit Y) we can go back and try unit X again. If unit Y has now been defined, the definition of unit X will succeed.

Yikes! How do we code such an animal? The happy answer is, we don't have to! I built a general-purpose forward referencing solution a while back that can do all the heavy lifting for us. To use this work, just a few minor changes and code insertions are needed:

 units.rb   a units pipe dream ```require 'forward_referencing' class Numeric def method_missing(method,*args) if Units.defining? reference = Units.make_forward_reference(method,Units.defining?) begin value = Units.convert self, method Units.release_forward_reference reference value rescue MissingUnitsException Units.hold_forward_reference rescue UnitsException => exception units_problem("definition",exception,method,args) rescue Exception method_missing_before_units(method,args) end else begin Units.convert self, method rescue UnitsException => exception units_problem("usage",exception,method,args) rescue Exception method_missing_before_units(method,args) end end end def units_problem(state,exception,method,*args) raise exception end end class Units extend ForwardReferencing start_forward_referencing def Units.convert(numeric,unit_identifier) if (candidates = lookup(unit_identifier)).size == 0 raise MissingUnitsException.new(unit_identifier.to_s) elsif !defining? if candidates.size > 1 raise AmbiguousUnitsException.new(unit_identifier.to_s) else unit = candidates NumericWithUnits.new(numeric,unit) end else units = candidates.select { |candidate| @@defining == candidate.units_system.units_measure } case units.size when 0 then raise MissingUnitsException.new(unit_identifier.to_s) when 1 then unit = units NumericWithUnits.new(numeric*unit.equals.numeric,unit.equals.unit) else raise AmbiguousUnitsException.new(unit_identifier.to_s) end end end @@holding = nil def Units.hold_forward_reference(hold = true) @@holding = hold end def Units.holding_forward_reference? @@holding end def Units.make_forward_reference(method,context) @@holding ? nil : create_forward_reference(method,context) end def Units.release_forward_reference(reference = nil) remove_forward_reference(reference) if reference != nil end def Units.establish_forward_reference_context(context) Units.defining context end def Units.clear @@measures.clear @@units.clear forward_references_clear self end end class UnitsMeasure def system(name,&block) Units.defining self system = (self[name] ||= UnitsSystem.new(self,name)) block.call system if block_given? Units.resolve_forward_references Units.defining nil system end end class UnitsSystem def unit(attributes) unit = nil if !Units.holding_forward_reference? unit = UnitsUnit.new self, attributes self[unit.name] = unit Units.add_unit unit else Units.hold_forward_reference nil end Units.continue_forward_reference_resolution unit end end ```

The changes are hardly even invasive! We just require the `forward_referencing` feature, and away we go. `Numeric`'s `method_missing` method is reorganized with respect to the defining context. If defining, we create a forward reference, try to convert, remove the forward reference if succesful and return the value. If the unit we needed was missing, we hold off on the unit definition. If some other `Units`-related problem occurred, we report it. Otherwise, we defer to the previous `method_missing`. If not defining, we try to convert and return the value if successul. If a `Units`-related problem occurred, we report it. Otherwise, we defer to the previous `method_missing`.

`Numeric`'s `units_problem` method simply re-raises the `UnitsException` that occurred. This would likely be overriden for domain-based handling. `Units` is made to extend forward referencing, and starts it up. (The working details of the mechanism are described below.) The `Units.convert` method is adjusted to raise the `MissingUnitsException` and the `AmbiguousUnitException` that are rescued in `Numeric`'s `method_missing` method.

The `holding` class variable is added to `Units` to indicate whether a definition should be added and associated or not based on the status of a forward reference. The `Units.hold_forward_reference` method sets the value of the variable, while the `holding_forward_reference?` method reports its value. What's important is that is we've gotten stopped during the definition, we don't want to add any other forward references until we get past it - otherwise we may save forward references that don't have a viable context for resolution later - that's why we have the `make_forward_reference` and `release_forward_reference` as holding-sensitive covers for the mixed-in `create_forward_reference` and `remove_forward_reference` methods. The `Units.establish_forward_reference_context` method asserts the given context as the defining context. `UnitMeasure`'s system method adds a call to `Units.resolve_forward_references` when the system block is finished to try to resolve any unresolved forward references that may have occurred. `UnitSystems`'s unit method is adjusted to be sensitive to forward reference holding, and adds a call to `Units.continue_forward_reference_resolution` to jump back into resolution when trying to resolve forward references.

What's happening is this: when we try to define a unit, the `equals` method depends on the resolution of the equality. The equality is in the form of `number.unit_identifier`. The `unit_identifier` is not a instance method in the `Numeric` class, so this fires the `method_missing` method. Since we're defining units, we create a forward reference with the expectation that there may be no unit associated with the method (that is, the `unit_identifier`) that was given. We then try to convert.

The `Units.convert` method looks up the given unit by identifier. If it couldn't be found, a `MissingUnitsException` is raised. Since we're defining, we qualify the units returned by the lookup against the measure being defined. If none of those match, then a `MissingUnitsException` is raised; if more than one was found, then an `AmbiguousUnitsException` is reased. If just one qualified, then a `NumericWithUnits` is created and returned.

Back in `Numeric,` if we didn't get a raise, we remove the forward reference because everything went fine, and we return the value. Now the `unit` method in `UnitsSystem` is called and the `UnitsUnit` is created and appropriately added. But if a `MissingUnitsException` occurred, `Numeric` places a hold on the system and proceeds. If some other type of `UnitsException` occurred (including an `AmbiguousUnitsException`) we're at a loss. We can't recover and we have to handle the problem outside of the system, so the `units_problem` method is called and the `UnitsException` is propagated.

The hold is interesting - the processing can't stop, and since the return type of calling hold is true, that's what gets returned from `method_missing`. It's not a `Numeric`, but that's okay - the `unit` method in `UnitsSystem` is called since we got through the `Numeric` with success. When the `UnitsSystem`'s `unit` method is entered, it checks to see if we're holding - which we are. It doesn't create the unit - it just turns off the hold. Since we bypassed all of the effort to get the unit in, we want to turn off the hold so we can try the next unit definition. In either case, we call the `continue_forward_reference_resolution` method, which at this point is a no-op. We then return the unit that was created.

Note that in this flow, we never removed the forward reference. `Units`, which has been extended by `ForwardReferencing` is accumulating all of these forward references for us. We keep defining and accumulating until finally, the block that's doing the unit definitions returns in `UnitsMeasure`. Now we call the `resolve_forward_references` method - this is where the magic happens! Inside this method, we go back and try to resolve each forward reference, one at a time.

When we get ready to try, the context we established when we created the forward reference (remember - way back at the beginning of `Numeric`) is re-asserted through the `establish_forward_reference_context` method in `Units`. Following this, the resolver jumps back to the spot where we created the forward reference, as if we were trying to handle the `equals` method again! If the unit we need is still undefined, we go through the same mechanism we went through. But if the unit we are looking for has been defined (remember, we didn't stop - we kept going and the definition of the unit we needed may have since happenned) now `Numeric` will successfully get the converted value, the forward reference will be removed, no hold will be made, and the unit will be created in `UnitSystem` and properly associated. When we now hit the `continue_forward_reference_resolution,` it's no longer a no-op, but instead jumps back into the `resolve_forward_references` method and tries the next forward reference.

Once we've run through all of the forward references, if we did resolve something, we try to resolve again - the resolutions we made may have created the units other forward references depended on. But if nothing got resolved, we've done all we can and we return. If there are any unresolved forward references left over, we can try them again the next time units are created in the `UnitsMeasure`.

While this may seem complicated, all the dirty work is going on behind the scenes. All we really had to do was add calls to create and remove forward references, start and continue forward reference resolution, and implement a hold mechanism to keep a unit from being defined if it had a forward reference. Nice and clean.

#### Greek Style

One of the big benefits of the metric units system is the greek prefix. The prefix coming before a base unit multiplies it by a power of ten, making it easy for us to abstract scale away from our numbers. For instance, it's much easier for us to think in centimeters or kilometers than meters for certain scales, the way we would with inches or miles in the english system. However, the conversions are simple in metric (100 cm = 1 m, 1000m = 1 km, 100,000 cm = 1 km) while more difficult in english (12 in = 1 ft, 5280 ft = 1 mi, 63360 in = 1 mi).

Because the greek prefixes can be applied to any unit, we can make them easy to create:

 units.rb   a units pipe dream ```class UnitsSystem def unit(attributes) unit = nil if !Units.holding_forward_reference? unit = UnitsUnit.new self, attributes self[unit.name] = unit Units.add_unit unit case unit.greek when :ten then greek_ten unit when :two then greek_two unit end else Units.hold_forward_reference nil end Units.continue_forward_reference_resolution unit end def greek_ten(base) greek_unit base, "yocto", "y", 0.000000000000000000000001 greek_unit base, "zepto", "z", 0.000000000000000000001 greek_unit base, "atto", "a", 0.000000000000000001 greek_unit base, "femto", "f", 0.000000000000001 greek_unit base, "pico", "p", 0.000000000001 greek_unit base, "nano", "n", 0.000000001 greek_unit base, "micro", "u", 0.000001 greek_unit base, "milli", "m", 0.001 greek_unit base, "centi", "c", 0.01 greek_unit base, "deci", "d", 0.1 greek_unit base, "deca", "da", 10.0 greek_unit base, "hecto", "h", 100.0 greek_unit base, "kilo", "k", 1000.0 greek_unit base, "mega", "M", 1000000.0 greek_unit base, "giga", "G", 1000000000.0 greek_unit base, "tera", "T", 1000000000000.0 greek_unit base, "peta", "P", 1000000000000000.0 greek_unit base, "exa", "E", 1000000000000000000.0 greek_unit base, "zetta", "Z", 1000000000000000000000.0 greek_unit base, "yotta", "Y", 1000000000000000000000000.0 end def greek_two(base) greek_unit base, "kilo", "k", 2**10 greek_unit base, "mega", "m", 2**20 greek_unit base, "giga", "g", 2**30 greek_unit base, "tera", "t", 2**40 greek_unit base, "peta", "p", 2**50 greek_unit base, "exa", "e", 2**60 greek_unit base, "zetta", "z", 2**70 greek_unit base, "yotta", "y", 2**80 end def greek_unit(base,prefix,abbrev_prefix,scalar) unit :name => prefix+base.name, :plural => prefix+base.plural, :abbrevs => base.abbrevs.collect { |abbrev| abbrev_prefix+abbrev }, :equals => base.equals * scalar end end ```

Hey - what's that `greek_two` method doing there? Well, there's also a common use of greek for powers of two because they're close to powers of ten (103 =~ 210). In fact with the advent of computer technology, the binary-based powers of two use of the prefixes is much a part of out lives as its decimal-based powers of ten precursor.

`UnitSystem`'s `unit` method is extended to greek the unit if the `greek` attribute is set. `UnitSystem`'s `greek_ten` method defines and creates the power-of-ten prefixes and scalar for a unit. `UnitSystem`'s `greek_two` method defines and creates the power-of-two prefixes and scalar for a unit. `UnitSystem`'s `greek_unit` method applies the given prefixes and scalar to a unit to create a new unit.

The reason that we define the powers of ten the way we do is so the roundoff in Ruby doesn't bite us. Take yocto and yotta for instance:

 10**24 - 10.0**24 ==> 0 10**-24 - 10.0**-24 ==> 1.83671e-40

This all despite the fact that

 10**-1 - 10.0**-1 ==> 0

A quick experiment

 (1..100).select { |i| 10**-i - 10.0**-i != 0.0 }

reveals that 23, 24, 25, 26, 28, 29, 32, 34, 39, 42, 45, 49, 50, 54, 56, 60, 63, 66, 72, 75, 81, 82, 84, 88, 91, 97 all have roundoff - that's over one quarter of just the first hundred powers of ten. A bug? Perhaps. But it is quite reproducible. We could delve deeper, but suffice to say that roundoff exists and expressing the number in greek_ten as the full sequence of digits tames the error.

The one thing that's still missing to make this work is scalar multiplication. In the `greek_unit` method, we have to be able to multiply a `NumericWithUnits` by a `Numeric` to get a new `NumericWithUnits`. While we're at it, we'll also throw in division. As we can see, when we multiply by a scalar, we're just multiplying the numeric part and leaving the units alone; we'll do the same with division.

 units.rb   a units pipe dream ```class NumericWithUnits def *(value) if value.kind_of? Numeric NumericWithUnits.new(numeric*value,unit) else raise UnitsException.new("units mismatch") end end def /(value) if value.kind_of? Numeric NumericWithUnits.new(numeric/value,unit) else raise UnitsException.new("units mismatch") end end end ```

For both methods, if it's a `Numeric` we're applying, we create a new instance with its numeric part modified. At this time, we raise a `UnitsException` if the value isn't `Numeric` - we'll adjust this shortly.

#### Equating Units in Derived Measures

Okay! There's one more stop in this part of our journey: derived units! How do we represent units whose measures are derived?

Well, it just so happens that we have most of the pieces in place - we just need to use the derivation we tucked away in the `UnitsMeasure` to rationalize the process. But before we do, we need to make it a little easier on ourselves and do some refactoring.

Right now, our `NumericWithUnits` holds a `Numeric` and a `UnitsUnit`. Derivations are based on a collection of units that are raised to powers and multiplied together. We can do this in a `Hash` that is analogous to the work we did previously to accomodate derived measures. In this case the keys are `UnitsUnit` instances and the values are powers. Since this envelopes our previous representation as a `Hash` with a single element whose key is a `UnitsUnit` associated with a value of 1, there's no loss of functionality.

Because we know that we're tucking away `UnitUnit`s and powers in this `Hash`, we'll specialize it to include some appropriate behaviors.

 units.rb   a units pipe dream ```class UnitsHash < Hash def initialize(unit = nil,power = 1) if unit if unit.kind_of? UnitsUnit self[unit] = power elsif unit.kind_of? UnitsHash unit.each { |k,v| self[k] = v*power } else raise UnitsException.new("invalid unit: #{unit}") end end end def to_s(numeric = 1,use_abbrevs = false) if size == 1 && (su = select {|k,v| v == 1}).size == 1 && !use_abbrevs su = su.to_a (numeric == 1)? su.name : su.plural else abbrevs_to_s end end def abbrevs_to_s p = select {|k,v| v > 0}.sort.collect {|k,v| "#{k.abbrevs}#{(v == 1) ? "" : "**#{v}"}"} n = select {|k,v| v < 0}.sort.collect {|k,v| "#{k.abbrevs}#{(v == -1) ? "" : "**#{-v}"}"} numerator = (p.size > 0)? p.join(" ") : (n.size > 0)? "1" : "" denominator = (n.size > 0)? ((p.size > 0)? " / " : "/")+n.join(" ") : "" "#{numerator}#{denominator}" end def power!(power) self.each { |k,v| self[k] = v*power } end def **(power) clone.power!(power) end def merge!(value,power=1) value.unit.each { |k,v| self[k] = (self[k] || 0) + v*power delete k if self[k] == 0 } self end def merge(value,power=1) clone.merge!(value,power) end alias :merge :* end ```

We've captured the laws of exponents in here, as well as hijacked and expanded the code for printing unit names. The `initialize` method returns a new instance built from a `UnitsUnit` or another `UnitsHash`, optionally raised to a power. If no arguments are given, an empty instance is created. The `to_s` method returns a string representation of the instance. The caller can optionally provide a `Numeric` to have pluralization considered, and also indicate whether unit abbreviations will be used - but only in the case of a non-derived unit with a power of 1; otherwise, abbreviations will be used. The `abbrevs_to_s` method returns a string representation of the instance using abbreviations, formed as a numerator of units with positive powers over a denominator of units with positive powers. The `power!` method extends the power of each unit by the given power. The `exponentiation` operator returns a copy of the `UnitsHash` with the power of each unit extended by the given power - this is effectively unit exponentiation. The `merge!` method coallesces a given `UnitsHash` into the instance, adding in members that don't exist, extending members that do exist, and removing members whose powers become zero. Of course, we could keep the zero powers around, but since anything raised to the zeroth power is 1, we really don't need them anymore. The `merge` method coallesces a given `UnitsHash` into a copy of the instance. We alias the multiplication operation to this method - this is effectively unit multiplication.

By pushing the unit string representation code into `UnitsHash` we can simplify `NumericWithUnits` a little.

 units.rb   a units pipe dream ```class NumericWithUnits def initialize(numeric,unit) @numeric, @unit = numeric, units_hash(unit) end def units_hash(unit) (unit.kind_of? UnitsHash)? unit : UnitsHash.new(unit) end def to_s "#{numeric} #{unit.to_s(numeric)}" end end ```

The `initialize` method sets the numeric as before, but calls the `units_hash` method to rationalize the unit argument. The `units_hash` method returns the argument if it is a `UnitsHash,` or otherwise creates a new `UnitsHash` built using it. The `to_s` method is simplified to return a string representation of the instance as the numeric and the String returned by the `UnitHash`'s `to_s` method.

We now have everything set to create derived units. Amazingly enough, this simply amounts to extending and defining methods in `NumericWithUnits`.

 units.rb   a units pipe dream ```class NumericWithUnits def *(value) if value.kind_of? Numeric NumericWithUnits.new(numeric*value,unit) elsif value.kind_of? NumericWithUnits extend_units(value,1) else raise UnitsException.new("units mismatch") end end def /(value) if value.kind_of? Numeric NumericWithUnits.new(numeric/value,unit) elsif value.kind_of? NumericWithUnits extend_units(value,-1) else raise UnitsException.new("units mismatch") end end def **(value) if value.kind_of? Numeric extend_units(nil,value) else raise UnitsException.new("units mismatch") end end def align(target) factor = 1 target_unit = UnitsHash.new target.unit.each do |tu,tv| su = target.unit.keys.select {|u| u.units_measure == tu.units_measure} if su.size == 1 factor *= (tu.equals.numeric/su.equals.numeric)**tv target_unit[su] = tv else target_unit[tu] = tv end end NumericWithUnits.new(numeric*factor,target_unit) end def extend(units,power) if !units NumericWithUnits.new(numeric**power,unit**power) else value = align(units) NumericWithUnits.new(value.numeric*(units.numeric**power), value.unit.merge(units,power)) end end end ```

We're basically expanding the multiplication and division operators to handle `NumericWithUnits`, adding an exponetiation operator, and throwing in a few methods to do the real work. The multiplication operator extends the instance by multiplying by the given `NumericWithUnits` value with a power of 1. The division operator extends the instance by multiplying by the given `NumericWithUnits` value with a power of -1. The exponentiation operator extends the instance by raising the instance to the given power. Note that we only raise to numeric powers - raising to a value with units is just a bit too exotic. The align method returns a new `NumericWithUnits` that represents the target with it's units aligned to the instance. What this means is that for each of the instance's constituent units, a unit in the target with the same measure is promoted to that unit and it's numeric part is appropriately scaled. This effectively pulls all units in the same measure together. The extend method does double duty, either raising the instance to a power by rasing the numeric and unit part of the instance each to a power (if the units argument is nil) or multiplying the instance by the given numeric with units raised to the given power. The units are aligned prior to multiplication to make sure we can eliminate any units that end up with powers of 0.

So there we are. We can now define derived units appropriately. That covers all of our bases for unit definition and we'll move on to using units.

### The Pipe Dream - Part 5 - Simply Units

Using numbers with units should be as simple as using numbers. They should do all of the same things as regular Ruby Numerics as transparently as possible.

#### Simple Output

Before we start using `Units`, we'll need a starting set of unit definitions to draw from. While I'm keen to drop in a whole load of definitions, I'll keep things simple here, and add definitions in later sections as needed while we move forward.

We'll use the length measure in the english system that we set up earlier. We already built inches, feet and yards so we'll just go with those as our base.

First we'll try a few simple identities:

 puts 72.inches ==> '72 inches' puts 6.feet ==> '6 feet' puts 2.yards ==> '2 yards'

This is just the sort of output we were looking for.

Note that I'll be using a representation here with Ruby code to the left of the arrow and output to the right in a schematized form (numbers are plain, hashes are surronded by curly braces, arrays by square brackets, string output by quotes, etc.) I'm just trying to be clear - if you know even a little Ruby, you'll get the idea.

#### Multiplication, Division and Exponentiation

Lets see if our multiplication, division and exponentiation do what they should too.

 puts 72.inches/10 ==> '7 inches' puts 6.feet*5 ==> '30 feet' puts 2.yards**2 ==> '4 yd**2'

Interesting. While the second two statements seemed to work fine, the result was truncated in the first. Shouldn't we have gotten `7.2 inches`? Well, yes and no. In the human world we expect the fraction, but Ruby is only doing what we tell it to do according to well-established rules. When we use integer arithmetic (`FixNum` instances in Ruby) we get truncation. To do what we want, we need to make our `NumericWithUnits` result use real numbers.

Fortunately, this is easy to do.

 puts 72/10 ==> '7' puts 72.inches/10 ==> '7 inches' puts 72.0/10 ==> '7.2' puts 72.0.inches/10 ==> '7.2 inches'

Take a look - we're consistent with Ruby arithmetic without units. This is precisely the kind of behavior we want! Let's try a few more things.

 puts 72.inches/2.feet ==> '3.0' puts 6.feet/2.feet ==> '3.0' puts 2.yards/2.feet ==> '3.0'

This makes sense - the result is the ratio between the two values - a unitless number. Also notice that here - where two `NumericWithUnits` are involved, we automatically get conversions to a real number. However, even though we can't see it, the value is a `NumericWithUnits`.

 puts (72.inches/2.feet).class.name ==> 'NumericWithUnits'

While this works - and it does need to continue to work - we probably should do a quick repair to `NumericWithUnits`'s extend method and get back a `Numeric` in these cases.

 units.rb   a units pipe dream ```class NumericWithUnits def extend(units,power) if !units NumericWithUnits.new(numeric**power,unit**power) else value = align(units) extended_numeric = value.numeric*(units.numeric**power) extended_unit = value.unit.merge(units,power) (extended_unit.size == 0)? extended_numeric : NumericWithUnits.new(extended_numeric,extended_unit) end end end ```

This is a route back to `Numeric` instances from `NumericWithUnits` intstances - a number without units really should be a `Numeric`. Appropriately, evaluating the expression now gives

 puts (72.0.inches/2.feet).class.name ==> 'Float'

Here's another thing:

 puts 12.inches*12.inches ==> '144.0 in**2' puts 12.inches*1.foot ==> '1.0 ft**2' puts 1.foot*12.inches ==> '144.0 in**2' puts 1.foot*1.foot ==> '1.0 ft**2'

The way things are set up, when multiplying (or dividing) the resulting units are aligned to those of the second value. The values are equalvalent, of course: 144 square inches is equal to 1 square foot since 12 inches is a foot. Take a look:

 puts (1.0.ft == 12.0.in) ==> 'false'

Huh? Wait a second. That's not right. We missed something - we have to align units so we aren't comparing apples and oranges. We need to clean up the `==` operator.

 units.rb   a units pipe dream ```class NumericWithUnits def ==(value) value = align(value) (value.numeric == numeric) && (value.unit.equal? unit) end end ```

Ok. Let's try again.

 puts (1.0.ft == 12.0.in) ==> 'true'

That's better. So, like we said, 144 square inches is equal to 1 square foot.

 puts (1.0.ft**2 == 144.0.in**2) ==> 'false'

What the heck? Let's take a closer look:

 puts 1.0.ft**2 ==> '1.0 ft**2' puts 144.0.in**2 ==> '20736.0 in**2'

Damn. See what's happening? `144**2 = 20736`. The `NumericWithUnits` `144.inches` is being created prior to raising it to the second power - which squares both the numeric and unit parts. Friends, this is an ugly one. In fact, there's no good way to deal with it - it's one of those shorthands humans use all the time that defy the rules of mathematical logic. We could handle it by applying the `**` operator to only the units part of the `NumericWithUnits`, but that doesn't work because we'd lose the ability to raise the whole instance to a power, and we don't want that to happen. What do we do?

Well, the first thing we'll do is make a longhanded version of our initializer.

 units.rb   a units pipe dream ```class NumericWithUnits def initialize(numeric,unit,power=1) @numeric, @unit = numeric, units_hash(unit)**power end end ```

This will at least let us get the right result during creation if we're fastidious enough.

 puts NumericWithUnits.new(144.0,Unit.length.english.inch,2) ==> '144.0 in**2'

Unfortunately, this is impossibly painful for regular use. If we look a little more closely at our original problem, we can see that what we get is

(144.0.inches)**2

instead of what we want

144.0*(inches**2)

because of the way Ruby handles the operator precedence. In fact, there aren't any operators we can define that get evaluated ahead of the dereference ('dot') operator. We need to resort to extreme measures and do something depraved and shameful. We need to define a specialized unit-exponentiation operator.

 units.rb   a units pipe dream ```class NumericWithUnits def ^(value) if value.kind_of? Numeric NumericWithUnit.new(numeric,unit,value) else raise UnitsException.new("units mismatch") end end end ```

Yeah, this is ugly - we're re-using the exclusive-or ('hat') operator as the power operator. But you know what? Tough. The exclusive-or is a logical, set-theoretical operator - it only has a loose interpretation in the numeric domain. In fact, we'll adopt a convention for units - we'll make them always use the 'hat' notation for unit powers.

 units.rb   a units pipe dream ```class UnitsHash def abbrevs_to_s p = select {|k,v| v > 0}.sort.collect {|k,v| "#{k.abbrevs}#{(v == 1) ? "" : "^#{v}"}"} n = select {|k,v| v < 0}.sort.collect {|k,v| "#{k.abbrevs}#{(v == -1) ? "" : "^#{-v}"}"} numerator = (p.size > 0)? p.join(" ") : (n.size > 0)? "1" : "" denominator = (n.size > 0)? ((p.size > 0)? " / " : "/")+n.join(" ") : "" "#{numerator}#{denominator}" end end ```

Finally, we have something that we can do the job for us.

 puts 1.0.ft^2 ==> '1.0 ft^2' puts 144.0.in^2 ==> '144.0 in^2' puts (1.0.ft^2 == 144.0.in^2) ==> 'true'

Note that we didn't replace exponentiation - we still need it! We just have a different symbol to use to just exponentiate units. The biggest problem we have by doing this is that the operator precedence gets messed up - the 'hat' has a precedence below the rest of arithmetic. This is really ugly - it means that something like

1.0.ft^2 * 1.0.ft

that would be a perfectly good expression to use to extrude a volume from an area would be evaluated as

1.0.ft^(2 * 1.0.ft)

since multiplication has higher precedence than exclusive-or. Unfortunately, because the precedence of Ruby operators is not configurable, there is no way to recover from this one without parenthesis. We need to do it like

(1.0.ft^2) * 1.0.ft

using parenthesis to enforce the correct evaluation order. Since many of the unit uses will be defined as variable expressions

a = 1.0.ft^2
l = 1.0.ft
v = a*l

this shouldn't be a problem most of the time. But in all honesty, I feel a little dirty after doing this.

There is another way to look at the problem that we need to consider:

144.0 * 1.inch**2

This is a valid expression that unfortunately wont't work. Why? Because, when the * operator gets called, it's getting called on the `Numeric`, not the `NumericWithUnits`. Let's go fix that up.

 units.rb   a units pipe dream ```class NumericWithUnits def NumericWithUnits.create_commutative_operators(klass) code = <

Here we're using a little bit of metaprogramming - we've written some code that writes code, and when evaluated adds method to a class. What the code does is modifies the computation when the argument is a `NumericWithUnits`, appropriately reversing the order of the operation; otherwise, everything is left alone. Since multiplication (and by using the reciprocal, division) is commutative, the result is independent of the order of the values. We create the operations for Ruby's fixed, big fixed and real number classes.

 puts 144.0 * 1.in^2 ==> '144.0 in^2' puts 2.0 / 3.0.ft^3 ==> '0.666666666666667 1/ft^3'

Sweet. It looks like everything's going fine! Multiplication and division work great, but we'll need parenthesis a little more often, when we use the 'hat' operation in an out-of-precedence position.

#### Addition and Subtraction

You might have thought we should have done addition before multiplication, but not so. The twist in addition (and subtraction) is that you can't add two numbers with units unless they have the same units - the result doesn't make sense mathematically. What does `3.feet + 2.seconds` mean? You just don't get a reasonable answer. Sure, we might be able to come up with something meaningful, but it'd be a stretch. We want to keep things making sense or this effort will be for naught.

Fortunately, we built most of the mechanicism we need to do this when we were working on multiplication. We define addition and subtraction and modify our align method to do just what we need. First we'll adjust the `align` method.

 units.rb   a units pipe dream ```class NumericWithUnits def align(target,all=true) factor = 1 target_unit = UnitsHash.new target.unit.each do |tu,tv| su = target.unit.keys.select {|u| u.units_measure.equal? tu.units_measure } if su.size == 1 factor *= ((1.0*tu.equals.numeric)/su.equals.numeric)**tv target_unit[su] = tv else target_unit[tu] = tv end end raise UnitsException.new("units mismatch") if all && (unit != target_unit) NumericWithUnits.new(numeric*factor,target_unit) end end ```

We'll add an argument to the method: `all` will indicate that the resulting units of the target must equal the instance's units. The code is the same except for the inclusion of raising a `UnitsException` if this condition is `false` . While we're at it, we'll also make sure we don't lose fractional parts when the numeric parts coming in are integers.

Now we can add the addition and subtraction operations, but we have to adjust our call to `align` in the `extend` method. We'll also throw in a unary plus and minus for compatability and to let us implement subtraction as addition.

 units.rb   a units pipe dream ```class NumericWithUnits def +@ clone end def -@ value = clone value.numeric = -(value.numeric) value end def +(value) if value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric += value.numeric aligned_value else raise UnitsException.new("units mismatch") end end def -(value) self + (-value) end def extend(units,power) if !units NumericWithUnits.new(numeric**power,unit**power) else value = align(units,false) extended_numeric = value.numeric*(units.numeric**power) extended_unit = value.unit.merge(units,power) (extended_unit.size == 0)? extended_numeric : NumericWithUnits.new(extended_numeric,extended_unit) end end end ```

Now when we try to add one `NumericWithUnits` to another, the units are aligned prior to performing the addition, to make sure the numeric part we're adding to has been scaled appropriately. If the units don't match, the `align` method throws a `UnitsException`.

 puts (2.0.ft^2) + (3.0.in^2) ==> '291.0 in^2' puts (1.0.yd^2) - (1.0.ft^2) ==> '8.0 ft^2'

Continuing on this path, does it make any sense to add a unitless value to a value with units? Technically no, but practically yes. In the real world, "5 inches plus 2" is 7 inches; the 2 is assumed to be in inches because people are smart and can make that connection. Of course programmers are smart people too and we want to make their lives easier, so we'll also mimic the human interpretation in our Ruby code. We just need a slight adustment in our `add` method.

 units.rb   a units pipe dream ```class NumericWithUnits def +(value) if value.kind_of? Numeric NumericWithUnits.new(numeric+value,unit) elsif value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric += value.numeric aligned_value else raise UnitsException.new("units mismatch") end end end ```

This gives us our desired behavior.

 puts 5.0.inches + 2 ==> '7.0 inches' puts 5.0.inches - 2 ==> '3.0 inches'

Of course, we should also make sure to add the commutative operations

 units.rb   a units pipe dream ```class NumericWithUnits def NumericWithUnits.create_commutative_operators(klass) code = <

So that

 puts 2 + 5.0.inches ==> '7.0 inches' puts 2 - 5.0.inches ==> '-3.0 inches'

works too, which despite being a little more tenuous in human usage should really be there for mathematical completeness. Otherwise, of course, someone will try it, get an error, and wonder why.

#### Comparison

A `NumericWithUnits` should, like its `Numeric` breathren, be comparable. We should be able to determine whether one is less than, greater than, less than or equal to, greater than or equal to, equal to or not equal to another. Ruby has a nice clean shorthand for this, the `compare` operator and companion `Comparable` module.

What we need to do is define the ``` <=>``` operator and mix in `Comparable`. The really neat thing is, since `Comparable` gives us the `==` method, we can remove it from the class and leverage the one that's provided by the module.

 units.rb   a units pipe dream ```class NumericWithUnits understands Comparable def <=>(value) if value.kind_of? Numeric numeric <=> value elsif value.kind_of? NumericWithUnits align(value).numeric <=> value.numeric else raise UnitsException.new("units mismatch") end end def NumericWithUnits.create_commutative_operators(klass) code = < def <=>(value) (value.kind_of? NumericWithUnits) ? (value <=> self) : old_compare(value) end alias :old_gt :> def >(value) (value.kind_of? NumericWithUnits) ? (value < self) : old_gt(value) end alias :old_lt :< def <(value) (value.kind_of? NumericWithUnits) ? (value > self) : old_lt(value) end alias :old_gteq :>= def >=(value) (value.kind_of? NumericWithUnits) ? (value <= self) : old_gteq(value) end alias :old_lteq :<= def <=(value) (value.kind_of? NumericWithUnits) ? (value >= self) : old_lteq(value) end alias :old_eq :== def ==(value) (value.kind_of? NumericWithUnits) ? (value == self) : old_eq(value) end operatorsEND klass.class_eval code end end ```

Notice that we included the unitless variants as well and expanded the commutative operations to include comparison. The question is why did we need to include the >, <, >=, <= and == in the commutative operations instead of just <=>? The answer is that for `Fixnum`, `Bignum` and `Float` instances, the individual operations are defined separately for performance reasons. We must override them to alter the behavior for `NumericWithUnits` instances - changing ``` <=>``` isn't enough.

 puts 13.inches <=> 1.foot ==> '1' puts 12.inches <=> 1.foot ==> '0' puts 11.inches <=> 1.foot ==> '-1' puts 13.inches > 1.foot ==> 'true' puts 12.inches > 1.foot ==> 'false' puts 11.inches > 1.foot ==> 'false' puts 13.inches < 1.foot ==> 'false' puts 12.inches < 1.foot ==> 'false' puts 11.inches < 1.foot ==> 'true' puts 13.inches >= 1.foot ==> 'true' puts 12.inches >= 1.foot ==> 'true' puts 11.inches >= 1.foot ==> 'false' puts 13.inches <= 1.foot ==> 'false' puts 12.inches <= 1.foot ==> 'true' puts 11.inches <= 1.foot ==> 'true' puts 13.inches == 1.foot ==> 'false' puts 12.inches == 1.foot ==> 'true' puts 11.inches == 1.foot ==> 'false'

Voila! We now implement comparison, with all the appropriate variants. You can try the unitless variants yourself if you'd like; the results are the same.

Since the code for that section is really smelling funny because it's geting so long, we'll do a quick refactoring.

 units.rb   a units pipe dream ```class NumericWithUnits def NumericWithUnits.commutative_operator(op,old,calc) ([] << "alias :old_#{old} :#{op}" << "def #{op}(value)" << " (value.kind_of? NumericWithUnits) ? #{calc} : old_#{old}(value)" << "end"). join("\r\n") end def NumericWithUnits.create_commutative_operators(klasses) commutative_operators = ([] << commutative_operator( "*", "multiply", "value * self" ) << commutative_operator( "/", "divide", "(value**-1) * self" ) << commutative_operator( "+", "add", "value + self" ) << commutative_operator( "-", "subtract", "(-value) + self" ) << commutative_operator( "<=>", "compare", "-(value <=> self)" ) << commutative_operator( ">", "gt", "(value < self)" ) << commutative_operator( "<", "lt", "(value > self)" ) << commutative_operator( ">=", "gteq", "(value <= self)" ) << commutative_operator( "<=", "lteq", "(value >= self)" ) << commutative_operator( "==", "eq", "(value == self)" )). join("\r\n") klasses.each { |klass| klass.class_eval commutative_operators } end end NumericWithUnits.create_commutative_operators [ Fixnum, Bignum, Float ] ```

That's much cleaner now.

#### Modulo

There's one other mathematical operator we need to include: the modulo operation, which returns the remainder of a division. This one isn't too bad, but it's too bizarre a calculation to do simply in commutative form - so we'll use a helper.

 units.rb   a units pipe dream ```class NumericWithUnits def %(value) if value.kind_of? Numeric NumericWithUnits.new(numeric % value,unit) elsif value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric = aligned_value.numeric % value.numeric aligned_value else raise UnitsException.new("units mismatch") end end def inv_mod(value) if value.kind_of? Numeric NumericWithUnits.new(value % numeric,unit) elsif value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric = value.numeric % aligned_value.numeric aligned_value else raise UnitsException.new("units mismatch") end end def NumericWithUnits.create_commutative_operators(klasses) commutative_operators = ([] << commutative_operator( "*", "multiply", "value * self" ) << commutative_operator( "/", "divide", "(value**-1) * self" ) << commutative_operator( "+", "add", "value + self" ) << commutative_operator( "-", "subtract", "(-value) + self" ) << commutative_operator( "%", "modulo", "value.inv_mod(self)" ) << commutative_operator( "<=>", "compare", "-(value <=> self)" ) << commutative_operator( ">", "gt", "(value < self)" ) << commutative_operator( "<", "lt", "(value > self)" ) << commutative_operator( ">=", "gteq", "(value <= self)" ) << commutative_operator( "<=", "lteq", "(value >= self)" ) << commutative_operator( "==", "eq", "(value == self)" )). join("\r\n") klasses.each { |klass| klass.class_eval commutative_operators } end end ```

This gives us

 puts 5.feet % 7.inches ==> '4.0 inches' puts 27.inches % 2.feet ==> '0.25 feet' puts 12.inches % 5 ==> '2 inches' puts 12 % 9.inches ==> '3 inches'

which is just what we expected.

#### Approximate Equality

A comparison operation that doesn't come as part of the Ruby core, but one that I've personally found to be extremely useful is comparing to see if two numbers are approximately equal to each other. Approximate equality is tested with the `approximately_equals?` method and the `=~` operator that are defined in my eymiha_math rubygem.

The problem occurs when we consider two values that are close to each other, but not exact. Consider the length of the diagonal of a two-foot square:

 a = (2.feet^2)**0.5b = 16.9705627476.inches puts a == b ==> 'false'

Yeah, right. The two values are equal for all practical purposes. The difference is miniscule.

 puts a - b ==> '8.77143691013771e-10 inches'

The idea behind approximate equality is that if the distance between two values is less than some "measurement error" threshhold (called epsilon in Mathematics) then for all practical purposes the two values are equal. If we download the eymiha_math gem and `require 'approximately_equals'` in our code, we can check the relationship with `Numeric`s. This can be easily extended to `NumericWithUnits`.

 units.rb   a units pipe dream ```require 'approximately_equals' class NumericWithUnits def approximately_equals?(value,epsilon=Numeric.epsilon) if value.kind_of? Numeric numeric.approximately_equals?(value,epsilon) elsif value.kind_of? NumericWithUnits align(value).numeric.approximately_equals?(value.numeric,epsilon) else raise UnitsException.new("units mismatch") end end alias :=~ :approximately_equals? def NumericWithUnits.create_commutative_operators(klasses) commutative_operators ||= ([] << commutative_operator( "*", "multiply", "value * self" ) << commutative_operator( "/", "divide", "(value**-1) * self" ) << commutative_operator( "+", "add", "value + self" ) << commutative_operator( "-", "subtract", "(-value) + self" ) << commutative_operator( "%", "modulo", "value.inv_mod(self)" ) << commutative_operator( "<=>", "compare", "-(value <=> self)" ) << commutative_operator( ">", "gt", "(value < self)" ) << commutative_operator( "<", "lt", "(value > self)" ) << commutative_operator( ">=", "gteq", "(value <= self)" ) << commutative_operator( "<=", "lteq", "(value >= self)" ) << commutative_operator( "==", "eq", "(value == self)" ) << commutative_operator( "=~", "approxeq", "(value =~ self)" )). join("\r\n") klasses.each { |klass| klass.class_eval commutative_operators } end end ```

The operation works just like equality, with a little fudge thrown in:

 puts a =~ b ==> 'true'

The epsilon value can be changed to adjust the approximate equality threshhold.

#### Other Niceties

While addition, subtraction, multiplication, division, exponentiation, modulo and comparison are all core mathematical operations for dealing with units, there are some more methods we need if we'd like them to act like regular `Numeric` types.

Happily we don't have to write them. Instead we'll get them by piggybacking on the numeric part of a `NumericWithUnits`, forwarding the call using `method_missing`.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) NumericWithUnits.new(numeric.send(method,*args),unit) end end ```

Using a smattering of calls we know are in `Numeric`,

 puts(10.4.inches).ceil ==> '11 inches' puts(-10.4.inches).ceil ==> '-10 inches' puts(10.4.inches).floor ==> '10 inches' puts(-10.4.inches).floor ==> '-11 inches' puts(10.4.inches).abs ==> '10.4 inches' puts(-10.4.inches).abs ==> '10.4 inches' puts(10.4.inches).round ==> '10 inches' puts(-10.4.inches).round ==> '-10 inches'

We're going to use the `method_missing` method a lot more, but we'll keep this as the last step to make sure we fall through to the underlying `Numeric`'s methods when we don't recognize the intended method.

#### Bypassing Type Checks

In our code we frequently need to check to see if we have the right kind of object before execute some code; we use the `kind_of?` method to do this. This discrimination violates the dynamic spirit of Ruby in a way - but it's needed because objects don't all behave the same way and we sometime really need to know what we're dealing with before we do something. Ruby advocates "duck typing" in which methods are called on objects and any fallout that happens is handled by the receiver. The terminology comes from the old expression, "if it walks like a duck, and quacks like a duck, it must be a duck."

In our case, if an object can respond to a method, it must be something that could handle the method. Doing this gives the receiver the opportunity to figure out how to perform the function is it's so inclined, either by calling other methods, delegating the call to another object, or creating new code to do the work. We want `NumericWithUnits` to act like a `Numeric`, and we've done that. For all practical purposes, `NumericWithUnits` is a `Numeric` duck.

The problem is that `NumericWithUnits` does not inherit from `Numeric`. This means that if some code interrogates an instance of `NumericWithUnits` to see if its a `Numeric`

10.inches.kind_of? Numeric

the value returned would be `false`. We'd like this to be `true` despite the inheritence. Fortunately, we can.

 units.rb   a units pipe dream ```class NumericWithUnits alias :old_kind_of? :kind_of? def kind_of?(klass) (numeric.kind_of? klass)? true : old_kind_of?(klass) end end ```

We redirect the request to the numeric because that's what makes the instance compatible with the `Numeric`. So now,

 puts 10.inches.kind_of? Numeric ==> 'true' puts 10.inches.kind_of? Fixnum ==> 'true' puts 10.0.inches.kind_of? Float ==> 'true' puts 10.0.inches.kind_of? String ==> 'false'

You have to love Ruby. Faking out the inheritence tree is just too darned cool.

Of course, this means that we need to rearrange some of our methods - whenever we discriminate between a `NumericWithUnits` and a `Numeric`, we need to check for the `NumericWithUnits` first since both of the `kind_of?` tests on a `NumeriWithUnits` will now return true.

 units.rb   a units pipe dream ```class NumericWithUnits def <=>(value) if value.kind_of? NumericWithUnits align(value).numeric <=> value.numeric elsif value.kind_of? Numeric numeric <=> value else raise UnitsException.new("units mismatch") end end def approximately_equals?(value,epsilon=Numeric.epsilon) if value.kind_of? NumericWithUnits align(value).numericapproximately_equals?(value.numeric,epsilon) elsif value.kind_of? Numeric numeric.approximately_equals?(value,epsilon) else raise UnitsException.new("units mismatch") end end def +(value) if value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric += value.numeric aligned_value elsif value.kind_of? Numeric NumericWithUnits.new(numeric+value,unit) else raise UnitsException.new("units mismatch") end end def *(value) if value.kind_of? NumericWithUnits extend(value,1) elsif value.kind_of? Numeric NumericWithUnits.new(numeric*value,unit) else raise UnitsException.new("units mismatch") end end def /(value) if value.kind_of? NumericWithUnits extend(value,-1) elsif value.kind_of? Numeric NumericWithUnits.new(numeric/value,unit) else raise UnitsException.new("units mismatch") end end def **(value) if value.kind_of? Numeric && !(value.kind_of? NumericWithUnits) extend(nil,value) else raise UnitsException.new("units mismatch") end end def ^(value) if value.kind_of? Numeric && !(value.kind_of? NumericWithUnits) NumericWithUnits.new(numeric,unit,value) else raise UnitsException.new("units mismatch") end end def %(value) if value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric = aligned_value.numeric % value.numeric aligned_value elsif value.kind_of? Numeric NumericWithUnits.new(numeric % value,unit) else raise UnitsException.new("units mismatch") end end def inv_mod(value) if value.kind_of? NumericWithUnits aligned_value = align(value) aligned_value.numeric = value.numeric % aligned_value.numeric aligned_value elsif value.kind_of? Numeric NumericWithUnits.new(value % numeric,unit) else raise UnitsException.new("units mismatch") end end end ```

This rearrangement preserves the correct behavior for the operations.

#### Promoting From Numerics

The last simple-use detail we need to deal with is making it easier to construct values with units in a generic method - basically, a way to deal with units as a variable that can be applied to numbers.

What we'll do is add one more method to `Numeric`, that will give us back a `NumericWithUnits`.

 units.rb   a units pipe dream ```class Numeric def unite(unit=nil,power=1,measure=nil) if !unit self else if (unit.kind_of? String)||(unit.kind_of? Symbol) units = Units.lookup(unit) units = units.select {|u| u.units_measure == measure} if units.size > 1 if units.size == 0 units_problem("usage",MissingUnitsException.new(unit),:unit=,unit) elsif units.size == 1 unit = units else units_problem("usage",AmbiguousUnitsException.new(unit),:unit=,unit) end elsif unit.kind_of? NumericWithUnit unit = unit.unit end NumericWithUnits.new(self,unit,power) end end end ```

The `unite` method is named for unit-extend, but could also have the meaning of unite as in uniting a `Numeric` with a unit. It's versatile, being able to handle a string or symbol to indicate a unit name, or the unit of a given `NumericWithUnit`, or through the `NumericWithUnits` initializer, a `UnitsUnit` or a `UnitsHash`. This method tries to give you a meaningful value if you give it anything that has almost any relation to a unit. And if the unit being passed in is `nil`, it returns the `Numeric`'s value.

 inch = Units.length.english.inch n = 10 puts n.unite inch ==> '10 inches' puts n.unite "inch" ==> '10 inches' puts n.unite :inch ==> '10 inches' puts n.unite 1.inch ==> '10 inches' puts n.unite 1.inch.unit ==> '10 inches' puts n.unite nil ==> '10'

Note that when a `String` or `Symbol` is given, the unit is looked up - which can result in a missing or ambiguous exception. If it's ambiguous, we've designed it to try to find the intended unit if a measure is provided as context.

There is one thing though - calling `unite` on a `NumericWithUnits` will fire its `method_missing` method and use the `unite` method in `Numeric`. Because the `unite` method is already sensative to units, we need to avoid this delegation and define a `unite` method in `NumericWithUnits`; otherwise given the way `unite` is defined in `Numeric,` we'll get an invalid result. The question is, what should the correct result be?

 units.rb   a units pipe dream ```class NumericWithUnits def unite(target_unit=nil,power=1,measure=nil) numeric.unite(target_unit,power,measure) end end ```

Since `unite` in `Numeric` is effectively "assigning" the given unit to it's value, we'll make `unite` do the same thing with a `NumericWithUnits` instance - it'll return a `NumericWithUnits` with the existing numeric with the given unit substituted - even returning just the numeric part if the target unit is nil.

 n = 10.feet puts n.unite inch ==> '10 inches' puts n.unite "inch" ==> '10 inches' puts n.unite :inch ==> '10 inches' puts n.unite 1.inch ==> '10 inches' puts n.unite 1.inch.unit ==> '10 inches' puts n.unite nil ==> '10'

Our unit-uniter can be used to quickly add and adjust units as needed.

### The Pipe Dream - Part 6 - Conversions

Now that we have the mechanisms in place to define and operate on unit values, it's time to figure out how to convert them. The goal is that given a `NumericWithUnits` instance, we need to be able to convert its units as painlessly as possible. We'll take two tacks here. In the first, we want explicit conversion we want to provide a generic method to do our work, and in the second we want a mechanism to strip down a method name that will call the generic mechanism. This way conversions can be done explictly or generically depending on need.

It's here that we'll start adding to `NumericWithUnits`' `method_missing` method in earnest.

#### Generic Conversion Facilities

The first thing we need to do is add a `convert` method. It should take units in much the same way as our `unite` method does and convert any existing units in a measure to the ones that are indicated. We'll build convert to return a new `NumericWithUnits` instance, and we'll also provide a `convert!` method to perform the conversion on the existing instance. While we're at it, we'll include the capability to pass in an `Array` of units - this makes it nice and easy to do conversions when we've built up complex unit structures.

 units.rb   a units pipe dream ```class NumericWithUnits def convert(target_units=nil) if target_units units_hash = UnitsHash.new if !(target_units.kind_of? Array) units_hash.merge!(1.unite(target_units)) else target_units.each { |target_unit| units_hash.merge!(1.unite(target_unit)) } end align(1.unite(units_hash),false) else clone end end def convert!(target_units=nil) result = convert(target_units) self.numeric, self.unit = result.numeric, result.unit self end end ```

Conversion is trivial since we've already written it into the `align` method. In order to perform many of the operations within a `NumericWithUnits`, we've had to `align` units first, which is exactly what a conversion does - just with a slightly different interface.

The only interesting thing we're doing here is building up a value to align with, since our `unite` method can now do the work of verifying our units for us and setting them up into a `UnitsHash`. Once we have this, we can just unite the the result and align the instance to it to get our converted value.

As an example, let's calculate a velocity from a distance and duration. First we'll add some more unit definitions.

 definitions.rb   a units pipe dream ``` Units.create :length do |m| m.system :english do |s| s.unit :name => :mile, :abbrev => :mi, :equals => 1760.yards end end Units.create :time do |m| m.system :base do |s| s.unit :name => :second, :abbrev => :sec s.unit :name => :minute, :abbrev => :min, :equals => 60.seconds s.unit :name => :hour, :abbrev => :hr, :equals => 60.minutes end end ```

With these in place, we'll use `Units` to solve a simple conversion problem: if we went 140 miles in 2 hours and 35 minutes, what was our average speed in feet per second?

 distance = 140.miles duration = 2.hours + 35.minutes velocity = distance/duration puts velocity.convert [1.foot, 1.second] ==> '79.48387090677419 ft / sec'

Nice. We didn't need to muck around with conversion factors or anything, we just expressed the quantities in Ruby and did our calculation. What's more, if we want to use some stock conversions or see other values and make other calculations, we can do all it quite easily.

 feet_per_second = [1.foot, 1.second] miles_per_hour = [1.hour, 1.mile] puts velocity.convert feet_per_second ==> '79.48387090677419 ft / sec' puts velocity.convert miles_per_hour ==> '54.1935483870968 mi / hr puts distance.convert 1.yard ==> '246400.0 yards' puts 5.days * velocity ==> '6503.22580645161 miles'

Taking another look at this, the thought occurs that passing an `Array` into `unite` would be good to add to the interface - it'd just create a `NumericWithUnits` with its resulting units as the ones identified by calling `unite` recursively for each element in the `Array`. Though this might not be critical for external unit users, it isn't without some additional utility. And when used internally, the `convert` method can be simplified dramatically.

A little judicious refactoring, and

 units.rb   a units pipe dream ```class Numeric def unite(unit=nil,power=1,measure=nil) if !unit self else if (unit.kind_of? Array) units = UnitsHash.new unit.each {|u| units.merge! 1.unite(u)} unit = units elsif (unit.kind_of? String)||(unit.kind_of? Symbol) units = Units.lookup(unit) units = units.select {|u| u.units_measure == measure} if units.size > 1 if units.size == 0 units_problem("usage",MissingUnitsException.new(unit),:unit=,unit) elsif units.size == 1 unit = units else units_problem("usage",AmbiguousUnitsException.new(unit),:unit=,unit) end elsif unit.kind_of? NumericWithUnits unit = unit.unit end NumericWithUnits.new(self,unit,power) end end end class NumericWithUnits def convert(target_units=nil) target_units ? align(1.unite(target_units),false) : clone end end ```

the `convert` method becomes just a simple a one-liner.

Now that we have our generic `convert` method out of the way, we can build an explicit handler through the method_missing method. We'll focus on a nice human way to describe conversions.

#### Human-Oriented Conversion Facilities

If you've used Rails, you've seen the ease with which data can be accessed through a human-oriented method naming strategy. For simple queries, methods indicate the columns that are being used to select records making code more readable and freeing the developers from the underlying details of database access. While units are typically less complex to deal with, we'd still like to give unit users with the same conveniences. We've done much of that already for unit definition, declaration and usage. We now need to go the same distance for conversion.

When dealing with conversions, there are three types of verbs we need to distinguish:

to - used to change the units of the existing instance.

in - used to return a new instance with the new units.

per - used to return a new instance with a modified form.

Let's look at each alternative in turn and build up code to support them.

Converting a value with units to a value with new units is analogous to what's done by our generic `convert!` method. What we need to do is figure out what units to convert to, tuck those into an `Array`, and pass it all off to `convert!` to do the work.

We'll make a unilateral decision here - unit names shouldn't contain underscores. While this may seem constraining, in practice it really isn't; unit names are typically short single words. That's the way they've evolved over time, and even when they start out with longer or compound names, they typically get shortened - people just don't like long names for units. What this lets us do is build method names that can be simply parsed in a `method_missing` method.

When we want to change the units of a value, let's say a velocity to miles and hours, the method

velocity.to_miles_and_hours

should do this for us. To make this happen, we need to turn this into

velocity.convert ['miles', 'hours']

in `method_missing`.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) s = method.to_s.split '_' if s == 'to' s.shift convert! s.select{|e| e != 'and'} else NumericWithUnits.new(numeric.send(method,*args),unit) end end end ```

It's easy - we just convert the method name to a `String,` split it into an `Array` on the underscores, and if the first element is 'to', we shift it off, get rid of any split parts that were 'and', and convert the instance's units.

 velocity = 55.feet / 1.second puts velocity ==> '55 ft / sec' velocity.to_miles_and_hours puts velocity ==> '37.5 mi / hr'

The order of the unit names in the method doesn't matter and we don't need to specify the 'and' - it's optional. The `to_miles_and_hours`, `to_hours_and_miles`, `to_miles_hours`, and `to_hours_miles` methods are all equivalent.

The in conversion is just like the to conversion except that the old instance is left untouched - a new `NumericWithUnits` is returned.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) s = method.to_s.split '_' if s == 'to' s.shift convert! s.select{|e| e != 'and'} elsif s == 'in' s.shift convert s.select{|e| e != 'and'} else NumericWithUnits.new(numeric.send(method,*args),unit) end end end ```

This gives us

 velocity1 = 55.feet / 1.second puts velocity1 ==> '55 ft / sec' velocity2 = velocity1.in_miles_and_hours puts velocity1 ==> '55 ft / sec' puts velocity2 ==> '37.5 mi / hr'

As you can see in this example, `velocity1` is unaffected by the call to the conversion. And also, as before, `in_miles_and_hours`, `in_hours_and_miles`, `in_miles_hours`, and `in_hours_miles` are all equivalent. Oh, and for that matter, we can use any of a unit's identifiers in the method names to get the same result: `in_mi_hr`, `in_mile_hour`, `in_mile_hours`, etc. It all just works.

The per conversion is a little less trivial. This one, like in returns a new `NumericWithUnits`, but the conversion must be completely aligned with the given units - and also admits the possibility of inversion. Inversion? Yes.

By inversion we mean that we can use `per` to flip the conversion upside down. Using our example, if we have a value in length/time we'd want to convert the units using `miles_per_hour` or `feet_per_second` methods. But we also want to be able to use `per` to get time/length by specifying `seconds_per_mile`, for instance. Units should know that we've completely specified the units and inverted the relationship. For this reason, a per conversion is a bit more complicated than a simple in.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) s = method.to_s.split '_' if s == 'to' s.shift convert! s.select{|e| e != 'and'} elsif s == 'in' s.shift convert s.select{|e| e != 'and'} elsif s.select{|e| e == 'per'}.size > 0 convert_per method else NumericWithUnits.new(numeric.send(method,*args),unit) end end def convert_per method ps = method.to_s.select{|e| e != 'and'}.join('_').split '_per_' raise UnitsException.new('invalid per method') if ps.size != 2 numerator = 1.unite(ps.split('_')) numerator_units = Set.new numerator.unit.keys denominator = 1.unite(ps.split('_')) denominator_units = Set.new denominator.unit.keys positives = unit.keys.collect{|k| unit[k] > 0 ? k : nil}.compact! negatives = unit.keys.collect{|k| unit[k] < 0 ? k : nil}.compact! numerator_positives = Set.new 1.unite(positives).align(numerator,false).unit.keys numerator_negatives = Set.new 1.unite(negatives).align(numerator,false).unit.keys denominator_positives = Set.new 1.unite(positives).align(denominator,false).unit.keys denominator_negatives = Set.new 1.unite(negatives).align(denominator,false).unit.keys if (numerator_units == numerator_positives) && (denominator_units == denominator_negatives) convert((ps+'_'+ps).split('_')) elsif (numerator_units == numerator_negatives) && (denominator_units == denominator_positives) convert((ps+'_'+ps).split('_'))**-1 else raise UnitsException.new('invalid per units') end end end ```

This may seem a little daunting, but it's really not so bad. First we make `method_missing` call the `convert_per` method if 'per' is found in the method name. When we enter `compare_per`, we make sure 'per' is between two sets of units. We pull out the first set and make the numerator, and the second we make the denominator. Next we separate out the sets of units in this instance that have positive or negative exponents. We then align our positives and negatives to the numerator and denominator - and then we compare. If the numerator and denominator are aligned to the positives and negatives respectively, we're a normal conversion. If the numerator and denominator are aligned to the negatives and positives respectively, we're an inverted conversion. Otherwise, we've got units measures in the instance that don't match the ones implied by the method, so we raise a `UnitsException`.

It's not trivial only because we have a couple layers of matching going on. But it does give us what we want.

 velocity = 55.feet / 1.second puts velocity.miles_per_hour ==> '37.5 mi / hr' puts velocity.minutes_per_mile ==> '1.6 min / mi'

We have to. We have in. We have per. But it's still got one small bit of clunckiness. If we were to say something like

102.inches.feet

it's pretty clear that what we want to do is to get back a new `NumericWithUnits` with a value of 8.5 feet - exactly what in would give us. Putting the in_ at the beginning of the method name shouldn't be strictly necessary. But how should we do this? We should create a new convert method that decodes the method name, matches it up with the units we have to make sure everything's valid and does the conversion. And then how do we funnel methods that aren't meant to be conversions to the numeric part? Easy. We just reorganize using a `begin-rescue` to encapsulate our conversions.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) begin s = method.to_s.split '_' if s == 'to' s.shift convert! s.select{|e| e != 'and'} elsif s == 'in' s.shift convert s.select{|e| e != 'and'} elsif s.select{|e| e == 'per'}.size > 0 convert_per method else convert s.select{|e| e != 'and'} end rescue NumericWithUnits.new(numeric.send(method,*args),unit) end end end ```

Sneaky, huh? We just assume it's a conversion and we try it! If it's not, that's okay because we'll raise an exception, be rescued and give the instance's numeric a try at the end.

You know though, we did uncover a small bit of ugliness when we were trying thing out here. If you look back you'll see that to get `55 feet per second`, we had to express it as

55.feet / 1.second

It'd be much more natural to be able to express this as

55.feet/second

Well, this is really quite simple now that we're so familiar with `method_missing`. We just have to push it onto `Object`'s `method_missing` method.

 units.rb   a units pipe dream ```class Object alias :method_missing_before_units :method_missing def method_missing(method,*args) begin 1.unite(method.to_s.split('_').select{|e| e!= 'and'}) rescue Exception method_missing_before_units(method,*args) end end end ```

And if we slip in another flourish, we can use the per form here quite naturally.

55.feet_per_second

We get this by pushing on `Numeric`'s method_missing code.

 units.rb   a units pipe dream ```class Numeric def method_missing(method,*args) if Units.defining? reference = Units.create_forward_reference(method,Units.defining?) begin value = Units.convert self, method Units.remove_forward_reference reference value rescue MissingUnitsException Units.hold_forward_reference rescue UnitsException => exception units_problem("definition",exception,method,args) rescue Exception method_missing_before_units(method,args) end else begin s = method.to_s.split('_').select{|e| e != 'and'} if s.select{|e| e == 'per'}.size > 0 ps = s.join('_').split '_per_' raise UnitsException('invalid per method') if ps.size != 2 unite(ps.split('_'))/(1.unite(ps.split('_'))) else unite s end rescue UnitsException => exception units_problem("usage",exception,method,args) rescue Exception method_missing_before_units(method,*args) end end end end ```

We're dividing the first part of the `per` expression by the second part if it's well formed, but notice that we took the liberty of simplifying the non-defining case considerably, just calling the `unite` method rather than going through `Units`' `convert` method as we did previously.

There's one more place where a per conversion is useful - when we want to obtain a pure conversion factor between two units in the same Measure. For instance, if we say

feet_per_mile

we want to get back a value of 5280. For this, we slip a little more code into `Object`'s `method_missing` method.

 units.rb   a units pipe dream ```class Object def method_missing(method,*args) begin s = method.to_s.split('_') if s.select{|e| e == 'per'}.size > 0 per_ratio method else 1.unite(s.select{|e| e!= 'and'}) end rescue Exception => exception method_missing_before_units(method,*args) end end def per_ratio method ps = method.to_s.select{|e| e != 'and'}.join('_').split '_per_' raise UnitsException('invalid per method') if ps.size != 2 value = (1.unite ps.split('_')) / (1.unite(ps.split('_'))) raise UnitsException.new("per ratio has units") if value.kind_of? NumericWithUnits value end end ```

If the method has a per in it, we split it and do a division and return the division. Two things to notice though. First, we raise an `UnitsException` if the result of the division has units - that is, if it's a type of `NumericWithUnits`. Remember, that when the result of the division of two values with units is unitless (as in this case when the Measures cancel out) that the result is demoted to a pure `Numeric`. And second, look closely at the division. Why are we dividing the second value by the first? Well, because when we say something like "feet per mile", what we really mean is mile.in_feet/feet. It doesn't make pure mathematical sense - it's just the way people say it! Remember that for people, it's the context that matters: when the measures are the same on both sides of the `per`, the result of the division is inverted. Per changes its meaning in different contexts.

### The Pipe Dream - Part 7 - Improved Rendering

Alas, our `to_s` method in `NumericWithUnits` is a bit lacking. Often, values in a measure are expressed not just in a single unit, but broken down into related pieces. For instance, while

 puts 155.25.lbs ==> '155.25 pounds'

is perfectly reasonable, we'd also like the ability to have it display as

 ==> '155 pounds 4 ounces'

or

==> '155 lb 4 oz'

This really isn't so hard to do - what's hard is figuring out a good way to express it. What we'll do is add the definition of formats to our infrastructure, and allow to_s to take a format as an argument. This strategy will let us define as many formats as we want, and they'll be carried right along with the unit definitions.

Since we only do this sort of formating within a given system of units within a measure, it make sense to define the functionality there. Let's consider the length measure and a way to get feet, inches, and thirty-seconds of an inch. What would be nice is something like

 units.rb   a units pipe dream ``` Units.create :length do |m| m.system :english do |s| s.format :name => :feet_inches_and_32s, :format => "#{whole_feet} #{remaining_inches_with_fraction 32}" end end ```

and then do

 puts 13.2890625.ft.format("feet_inches_and_32s") ==> '13 ft 3-15/32 in'

Yeah, that would be really nice. Unfortunately, it does have a few problems, not the least of which the `String` itself would be evaluated during definition - even if we could come up with good definitions for getting whole and remaining parts (and whatever other interesting types of slicing and dicing we might need) of a `NumericWithUnits` in the right order. No, unfortunately an abstraction like this is just a little too abstract. What should we do?

Well, since we're in Ruby, why not use Ruby? Let's see. Instead of defining a `String` that gets evaluated when the system is being defined, let's use a closure - basically a nice Ruby way to define a function - and see where that gets us. The `lambda` method takes a block and converts it to a callable proc; the proc is Ruby's mechanism for closures.

 units.rb   a units pipe dream ``` Units.create :length do |m| m.system :english do |s| s.format :name => :feet_inches_and_32s, :format => lambda { |u| "#{u.whole_feet} #{u.remaining_inches_with_fraction 32}" } end end ```

Ok, so far so good. But how about those other pesky functions in there? There could be lots of them! How many of those are we going to have to write?

Well, the short answer is none, unless there's a good reason. What we'll do is just add processing to the block.

 units.rb   a units pipe dream ``` Units.create :length do |m| m.system :english do |s| s.format :name => :feet_inches_and_32s, :format => lambda { |u| ru = u.round_to_nearest(1.inch,32) feet = ru.feet.floor inches = (ru-feet).inches "#{feet} #{inches.with_fraction 32}" } end end ```

Hey - that's pretty close! We get to use most of what we already had. It looks like we need to just add a specialy method that will render the numeric part of the unit a little differently. The code will be cake!

Let's start writing...

 units.rb   a units pipe dream ```class UnitsSystem attr_reader :formats def initialize(units_measure,name) @units_measure = units_measure @name = name @formats = MethodicHash.new end def format(options) @formats[options[:name]] = options[:proc] end end class NumericWithUnits def format(name=nil) if name == nil to_s else raise UnitsException.new("system not explicit") if (system == nil) format = system.formats[name] raise UnitsException.new("missing format") if format == nil format.call(self) end end ```

Oops. Our definition of the format in `UnitsSystem` looks fine, but our call to `system` qualifying the first raise is a big problem. Consider - a measure is independent of systems! For example, the same length measured in the English system is equivalent to that length measured in the Metric system - this is just a way of rendering! While it's fine to define formats at the system level, they should be accessed through the measure level. Let's do a little quick change.

 units.rb   a units pipe dream ```class UnitsMeasure attr_reader :formats def initialize @formats = MethodicHash.new end def format(options) @formats[options[:name]] = options[:format] end end class UnitsSystem def format(options) @units_measure.format(options) end end class NumericWithUnits def format(name=nil) if name == nil to_s else raise UnitsException.new("measure not explicit") if (measure == nil) format = measure.formats[name] raise UnitsException.new("missing format") if format == nil format.call(self) end end ```

This is interesting. By coding it this way, storing the formats in the `UnitsMeasure` and defining a `format` method there, and calling that method from a `UnitsSystem` using a `format` method that takes the same arguments, we can potentially define formats at the `UnitsMeasure` level as well as within a `UnitsSystem`. Of course, we need to define the `measure` method in `NumericWithUnits`, but this is as easy as combining the `UnitMeasure`s of the `Units` in the `UnitsHash`. We'll build up a measure derivation and, since we need to make sure the "artificial" derivation matches a measure, we must enhance the `find_by_derivation` method in `Units`.

 units.rb   a units pipe dream ```class UnitsHash def measure measure = UnitsMeasure.new each { |unit,power| measure.merge_derivation unit.units_system.units_measure => power } Units.find_by_derivation(measure.derived) end end class Units def Units.find_by_derivation(derivation) matches = @@measures.values.uniq.select { |measure| if measure.derived measure == derivation elsif derivation.size == 1 a = derivation.to_a a == measure && a == 1 else false end } case matches.size when 0 then nil when 1 then matches else raise UnitsException.new( "Multiple UnitsMeasures with same derivation found") end end end ```

The remainder of work is specifying any additional renderers for the numeric part of a `NumericWithUnits`. But there is some great fallout from this. When we think about it, whatever we'd like to do to the numeric part, we'd like to be able to do to a `Numeric`. This part of the work on `Numeric` actually transcends units and we'll take a general approach to the solution. Lets code our `round_to_nearest` and `with_fraction` methods.

We'll first throw together our fraction printer and specialized rounder.

 units.rb   a units pipe dream ```class Numeric def with_fraction(denominator=1,separator='-',show_zero=false) sign = self <=> 0 unsigned = self*sign whole = (((unsigned*denominator).round)/denominator).floor numerator = ((unsigned-whole)*denominator).round "#{sign*whole}#{(numerator != 0)||show_zero ? "#{separator}#{numerator}/#{denominator}" : ""}" end def round_to_nearest(denominator=1) ((self*denominator).round)/(1.0*denominator) end end ```

Aside from some generality and the fact that because we're using the `floor` function we have to pay attention to the sign of the numeric, our `with_fraction` prints whole and fractional parts of numbers pretty nicely.

==>==> puts 9.1.with_fraction(50) '9-5/50' puts 9.0.with_fraction(25) '9' puts 9.0.with_fraction(25.5,' ',true) ==> '9 0/25.5'

The whole reason we need a `round_to_nearest` method is to ensure we get the right values all the way up to the highest unit. Hierarchical units are like the odometer on your car: rounding may cascade up to push you up to the next unit. If you don't take care, and just render the pieces separately before you rationalize the whole numeric, you may not do rounding correctly.

 puts 1.999.ft.inch.round_to_nearest(32) ==> '24.0 inches' puts 1.996.ft.inch.round_to_nearest(32) ==> '23.9375 inches'

See? If we just did a straight rounding on the feet or the inches, we'd lose the 32nds. It's just these sort of methods that, while we want to use them for formatting `NumericWithUnit` values, they make good sense to just add to the `Numeric`

From here it's just a short hop to get what we need - but like any other method in `Numeric` we want to use from a `NumericWithUnits`, we'll call the method on the numeric part from the `method_missing`. The difference is, we'll take a look at what the call returns: if it's a kind of `String` value, we'll tack on the units.

 units.rb   a units pipe dream ```class NumericWithUnits def method_missing(method,*args) begin s = method.to_s.split '_' if s == 'to' s.shift convert! s.select{|e| e != 'and'} elsif s == 'in' s.shift convert s.select{|e| e != 'and'} elsif s.select{|e| e == 'per'}.size > 0 convert_per method else convert s.select{|e| e!= 'and'} end rescue Exception value = numeric.send(method,*args) if (value.kind_of? String) "#{value} #{unit.to_s(numeric)}" else NumericWithUnits.new(value,unit) end end end end ```

So, let's give it a shot.

 length = 13.28090635.ft puts length.format :feet_inches_and_32s ==> '13 feet 2-12/32 inches'

Now before you start casting aspersions, I already know there are easy holes to poke into this. But look, with the power of Ruby behind you, you can write your own complex-but-generalized rendering methods and keep them associated with the unit definitions - not buried in custom code. Rendering is just as much a part of a `Units` package as all of the arithemtic and conversions, and now everything is brought together in such a way that we don't have to sweat it as much.

#### Rendering Derived Units

One of the things that has been overlooked until now is that we don't handle derived units correctly. In our headlong rush to add functionality, this got overlooked. While we can do:

 puts 225.ml ==> '225 milliliters'

 puts 225.ml/5.cm ==> '45 ml / cm'

we don't get the reduction from `ml/cm` to `cm^2` that we should have expected. What did we miss? Where did we go wrong?

Well, in truth, we didn't go wrong anywhere and we didn't miss anything - we just didn't write tests for derived unit capabilities. If we were starting from behavioral specifications, we probably would have captured this. But we weren't starting from specs. Remember, this is a pipe dream. Should we have written specs first? Perhaps. But this ignores one of the fundamental truisms of human endeavor: You almost never get it right. Translated to the problem at hand: if we had spec'ed it first, we would have missed something. If not this behavior, we would have missed something else. Why do anything in the first place then, you may ask, if we're never going to get anything right? Because two other fundamental truisms come into play, namely: Nothing gets done if you give up and Perfectionism is too expensive

If we look closely at the problem, the answer is fine - it's just that we really want an alternative that is reduced for calculating and the option to create an unreduced version for output. So, what do we do to fix this? We'll dive in and approach the issues we find one at a time. First, we'll look at the conversion problem above.

We like the declarative statement just fine. When we set the unit to be milliliters, we want to get milliliters back out. It's the conversion that's troublesome. When we divided by centimeters, we wanted the milliliters to be treated as a volume from which we could factor out a length. The problem seems to be that even though we're dealing with a unit derived from length, we don't take that into account during calculation. What we need to do is expand the units we're calculating with whenever we've got a derived unit.

 units.rb   a units pipe dream ```class UnitsHash def derived? keys.select{|unit| unit.units_system.units_measure.derived != nil}.size > 0 end def reduce factor = 1.0 new = UnitsHash.new each do |unit,power| if unit.units_system.units_measure.derived != nil factor *= unit.equals.numeric new.merge!(unit.equals,power) else new.merge! unit end end factor.unite new end end class NumericWithUnits def derived? unit.derived? end def reduce numeric*unit.reduce end def <=>(value) if derived? reduce <=> value elsif value.kind_of? NumericWithUnits if value.derived? self <=> value.reduce else align(value).numeric <=> value.numeric end elsif value.kind_of? Numeric numeric <=> value else raise UnitsException.new("units mismatch") end end def approximately_equals?(value,epsilon=Numeric.epsilon) if derived? reduce.approximately_equals?(value,epsilon) elsif value.kind_of? NumericWithUnits if value.derived? self.approximately_equals?(value.reduce,epsilon) else align(value).numeric.approximately_equals?(value.numeric,epsilon) end elsif value.kind_of? Numeric numeric.approximately_equals?(value,epsilon) else raise UnitsException.new("units mismatch") end end def +(value) if derived? reduce+value elsif value.kind_of? NumericWithUnits if value.derived? self+value.reduce else aligned_value = align(value) aligned_value.numeric += value.numeric aligned_value end elsif value.kind_of? Numeric NumericWithUnits.new(numeric+value,unit) else raise UnitsException.new("units mismatch") end end def *(value) if derived? reduce*value elsif value.kind_of? NumericWithUnits if value.derived? self*value.reduce else extend(value,1) end elsif value.kind_of? Numeric NumericWithUnits.new(numeric*value,unit) else raise UnitsException.new("units mismatch") end end def /(value) if derived? reduce/value elsif value.kind_of? NumericWithUnits if value.derived? self/value.reduce else extend(value,-1) end elsif value.kind_of? Numeric NumericWithUnits.new(numeric/value,unit) else raise UnitsException.new("units mismatch") end end def %(value) if derived? reduce%value elsif value.kind_of? NumericWithUnits if value.derived? self%value.reduce else aligned_value = align(value) aligned_value.numeric = aligned_value.numeric % value.numeric aligned_value end elsif value.kind_of? Numeric NumericWithUnits.new(numeric % value,unit) else raise UnitsException.new("units mismatch") end end def inv_mod(value) if derived? reduce.inv_mod value elsif value.kind_of? NumericWithUnits if value.derived? self.inv_mod value.reduce else aligned_value = align(value) aligned_value.numeric = value.numeric % aligned_value.numeric aligned_value end elsif value.kind_of? Numeric NumericWithUnits.new(value % numeric,unit) else raise UnitsException.new("units mismatch") end end end ```

We need to check the operands of any numeric operation where another `NumericWithUnits` is involved, reducing each prior to applying the operation if they are derived. For other operations, like exponentiation or ones that just operate on a `Numeric`, we can leave things alone since they don't require a reduction. Doing reduction this way means we'll adjust our values to underived units whenever we do calculations.

Let's see how we're doing so far:

 a = 225.mlb = 5.cmc = 15.cm^3 puts "#{a+c}, #{c+a} ==> '240.0 cm^3, 0.00024 m^3' puts "#{a-c}, #{c-a} ==> '210.0 cm^3, -0.00021 m^3' puts "#{a*b}, #{b*a} ==> '1125.0 cm^4, 1.125e-05 m^4' puts "#{a/b}, #{b/a} ==> '45.0 cm^2, 222.222222222222 1/m^2'

The numbers are right and the units match the rules we laid down about what unit takes precedence in an operation - everything looks good. So, now we just need to switch to the intended units during conversion. Let's work on a value in cubic centimeters and try to convert it milliliters.

 puts c.in_ml ==> '15 cm^3' puts c.to_ml ==> '15 cm^3'

No luck, but we expected that. Our problem is that the code doesn't look at the units we're derived from - we need to use what they're equal to and "back-convert". To do this we must create a more sophisticated alignment when we're working with derived units, keeping our non-derived-unit version around for fast performance.

 units.rb   a units pipe dream ```class NumericWithUnits def self.derived_align_type=(type) @@derived_align_type = type end def self.derived_align_type @@derived_align_type end @@derived_align_type = :whole_powers def align(target,all=true,type=@@derived_align_type) if !derived? && !target.derived? simple_align(target,all) else power_align(target,all,type) end end def simple_align(target,all=true) factor = 1 target_unit = UnitsHash.new unit.each do |tu,tv| su = target.unit.keys.select {|u| u.units_measure.equal? tu.units_measure } if su.size == 1 factor *= ((1.0*tu.equals.numeric)/su.equals.numeric)**tv target_unit[su] = tv else target_unit[tu] = tv end end raise UnitsException.new("units mismatch") if all && (target.unit != target_unit) NumericWithUnits.new(numeric*factor,target_unit) end def can_align(target,exact=true) ru, tru = reduce.unit, target.reduce.unit !exact || (ru == tru) end def reduce_power(p,f,type) if type == :whole_powers pa = p.abs (p == 0) ? [p,f,0] : (pa < f) ? [p, f, pa/p] : [p, f, p/f] else (p == 0) ? [p,f,0] : [p, f, (1.0*p)/f] end end def common_power(ps) ps.values.collect {|a| a}.inject {|p,e| p.abs < e.abs ? p : e} end def revise_power(rp,p) [rp, rp, p, rp-p*rp] end def power_align(target,all=true,type=@@derived_align_type) raise UnitsException.new("units mismatch") unless can_align(target,all) factor = 1 target_unit = UnitsHash.new unit_reduce = reduce target.unit.each do |tu,tv| t_u = {} tt_u = {} tu_reduce = tu.equals.reduce found = tu_reduce.unit.each do |ttu,ttv| su = unit_reduce.unit.keys.select {|u| u.units_measure.equal? ttu.units_measure } break false if su.size == 0 tt_u[ttu] = reduce_power(unit_reduce.unit[ttu],ttv,type) end if found cp = common_power(tt_u) tt_u.each {|k,v| tt_u[k] = revise_power(v,cp)} t_u[tu] = tv**cp t_factor = tu_reduce.numeric**cp target_unit[tu] = cp factor *= t_factor/(tu.equals.numeric**cp) tt_u.each {|k,v| unit_reduce.numeric /= t_factor unit_reduce.unit[k] = v} end end unit_redux = unit_reduce.simple_align(self,false) result_unit = target_unit.merge(unit_redux) raise UnitsException.new("units mismatch") if all && (target.unit != result_unit) NumericWithUnits.new(factor*unit_redux.numeric,result_unit) end end ```

Now when we `align`, we check to see if either the aligner or alignee is derived. If not, we just do a `simple_align`; otherwise, we do a `power_align`.

The `power_align` method uses reduced units to perform an alignment. First we check to make sure that we can do an alignment - if not, we fail abruptly. Then we walk through the set of target units; we reduce each in turn and look to if there are sufficient reduced units left to convert in the instance we're aligning to do a conversion of that unit. If so, we add the target, and reduce the instance's reduced units appropriately. At the end, we merge in the remaining reduced units after they've been re-aligned with the instance's original units. Of course, if we had to align all the units - because we're adding things together, for instance - we'll fail if things didn't come out right.

Other than this, there is one variation we wish to maintain when we power align - to use whole or fractional units. The choice really is arbitrary for calculation, but when rendering some would consider whole-number-dimensioned units to be prettier than fractionally-dimensioned units. We allow the method that will be used to be set at the class level for convenience.

 puts c.in_ml ==> '15.0 milliliters' puts c.to_ml ==> '15.0 milliliters'

And what's more,

 puts 225.ml/5.cm ==> '45.0 cm^2' puts (225.ml/5.cm).in_ml ==> '45.0 ml / cm'

Triumph! But there is a small consideration that we still need to take care of. Consider doing a conversion where you don't want to exhaust a measure too early - if you did, you'd have nothing left to draw from when aligning to subsequent targets. This may not happen very often, but it still could nonetheless. The trick is that we need to take the power of the resultant units into consideration during the alignment.

What we need is a way to specify the precise units and powers we'd like the resulting `NumericWithUnits` to have. For this, we'll call up the `align` method directly and give it an `Array` of the pieces to which we want to be aligned. For example, let's say we want milliliters over meter. We would want to do

 puts (225.ml/5.cm).align([1.ml,1/m])

We just need to take an `Array` of `NumericWithUnits` as a target and use each one (and it's power) to form the result.

 units.rb   a units pipe dream ```class NumericWithUnits def align(target,all=true,type=@@derived_align_type) if target.kind_of? Array piece_align(target) elsif !derived? && !target.derived? simple_align(target,all) else power_align(target,all,type) end end def piece_align(pieces,type=@@derived_align_type) factor = 1 target_unit = UnitsHash.new unit_reduce = reduce pieces.each do |p| p.unit.each do |tu,tv| t_u = {} tt_u = {} tu_reduce = tu.equals.reduce found = tu_reduce.unit.each do |ttu,ttv| su = unit_reduce.unit.keys.select {|u| u.units_measure.equal? ttu.units_measure } break false if su.size == 0 tt_u[ttu] = reduce_power(unit_reduce.unit[ttu],ttv,type) end if found tt_u.each {|k,v| tt_u[k] = revise_power(v,tv)} t_u[tu] = tv t_factor = tu_reduce.numeric**tv target_unit[tu] = tv factor *= t_factor/(tu.equals.numeric**tv) tt_u.each {|k,v| unit_reduce.numeric /= t_factor unit_reduce.unit[k] = v} end end end raise UnitsException.new("units mismatch") if unit_reduce.unit.has_units? NumericWithUnits.new(factor*unit_reduce.numeric,target_unit) end end class UnitsHash def has_units? (self.select {|k,v| v != 0.0}).size > 0 end end ```

Now we run the power reduction for each piece, and instead of asking for the common power, we just use the power that was passed in. At the end, if there are any units left over, we throw an exception. For this, we want an exact match.

 puts (225.ml/5.cm).align [1.ml,1/m] ==> 4500.0 ml / m

It's arguable that we should try to use the `convert` method instead of exposing the `align` method directly. However, there's something more subtle going on here - specifically, we don't want the elements of the array combined before we align, which is what `convert` does. We want each applied in turn, incrementally aligning the object to each element of the `Array`. We may not do it often, but when we want to output in particular units that don't come out of the calculations naturally, this will do the job.

Now we have all we need to render derived units correctly.

#### Just the Right Unit

When I've used units in a program's output up to now, most of the time they've been specified to me by someone else, or in a particular problem. "Answer the problem in meters per second," or "express the weight in pounds", or "how many tablespoons in a cup?" Sometimes, the choice is based on convention, sometimes on preference, and sometimes it's ad hoc. When we're given the choice, which units do we choose?

This is a good question - but because choice is involved, getting a answer can be tricky. The best we can really do is allow the user to give us some options and constraints and try to make a reasonable determination.

Since all the alternatives to choose from are effectively equivalent - all equal to each other as far as measurement is concerned - the constraints are based on weighting the units and the value of the numerics. For instance, if a units user were to present us with four unit choices each weighted by preference, and a preferred range for the numeric part of our `NumericWithUnits`, we should be able to return the units that provide the best fit.

What we'll do is set up a mechanism that takes constraints, weights and an `Array` of sample values that will return the ranked alternatives.

 units.rb   a units pipe dream ```class Units def Units.rank(unit_choices = {}, samples = [], &numeric_ranker) if block_given? scores = {} samples.each {|s| unit_choices.each {|uc,w| puts s.convert(uc) scores[uc] = (scores[uc] || 0) + w*(yield s.convert(uc).numeric) } } scores.sort {|e1,e2| -(e1 <=> e2)} scores.collect {|s| s} else rank(unit_choices,samples) { |n| e = ((((n.abs)-5.5).abs-4.5).at_most(0)) (e == 0) ? 0 : (e == 1) ? 0 : -(e + 1/(1-e)) } end end end ```

Alright. A little explanation is in order. We pass in a `Hash` of unit choices and a weight multiplier for each choice. For the sake of illustration, we'll pick

 choices = { 1.meter/min => 2, 1.mile/hour => 1, 1.km/hr => 1 }

What you see here is that scores for meter/minute are weighted twice as heavily as scores for miles/hour or kilometers/hour. The position we've taken is that the highest score wins, so scores should be more negative the further their values are from the ideal scheme - I've set it up so everything non-zero will be a negative, errors are the only values that will have weight. However, the algorithm is completely general - you can make it do whatever you'd like it to do.

Next we throw together a few samples:

 samples = [ 7.35.in/sec 1.18.in/sec, 0.92.in/sec ]

The idea is that these are values are representative for what we expect to get when we want "just the right units". We may have a sample set containing one element or a thousand - it doesn't matter. The unit that scores highest overall will be ranked first.

We've set up the algorithm to take the choices and samples, and a block that will give us a score based on the numeric part of a sample value when it's units are converted to one of the choices. As you can see, if we don't provide a block, the method will be re-sent with the curious-looking block provided. What this block does is scores zero if a numeric is between one and ten, and a value of `-(e + 1/(1-e))` when it's outside that range. The idea is that values with a single leading digit are preferred - what this does is makes the error value from one down to zero look like the range from 10 up to infinity - the farther you are from the range logarithmically speaking, the greater the error, and the weight magnifies the resulting error. We also exclude a plain zero (when `e == 1`) since it's a single digit.

So, we give it a try and

 puts Units.rank(choices,samples) ==> '1 m / min''1 km / hr''1 mi / hr'

So meters/minute is the best choice for this particular set of samples - despite the fact that we weighted the error twice as high! Given our samples, meters/minute is just the right unit to use.

Oh, by the way, you may not be familiar with `at_most` (or `at_least`.) No problem. They were schemed up to provide the max and min functionality available in most numerical libraries - `at_most` limits the a value on the upside, `at_least` limits it on the downside.

 units.rb   a units pipe dream ```class Numeric def at_most(m=0) m < self ? m : self end def at_least(m=0) m > self ? m : self end end ```

Kudos to Ruby for at least being extensible enough to add functionality that's missing. It'd be nice to have everything you could ever want already baked into the language, but at least you aren't locked out from adding the parts you need.

### The Pipe Dream - Part 8 - The Need for Speed

I'll be the first to admit we've added a lot of goo to numbers in Ruby. While the goo is good for making values with units actually work, it's going to slow computations down at least a little. I think we all need the unit-awareness, and it's incredible useful to have it at our fingertips, but how gooey is Ruby now? There's all the goo for units themselves, and method_missing, and... hmmm... let's take a look at how big the hit the units goo really is.

#### Working with Unitless Values

The best way to look at this is, I suppose, is to consider what happens when we're working with unitless values. We should be satisfied with the system when the values we're working with do have units - but we need to make sure that when they don't that we didn't handcuff ourselves.

So lets take stock here. We'll look at the methods we've added or changed that would get called if we're operating on unitless values in unitless contexts.

 Ruby Object Method Effect Numeric `method_missing` the case we're not defining units, we look for methods with '_per_' in their name and failing that, we try to use the name of the method to convert the object to a value with units. Otherwise the processing goes to the prior, overriden `method_missing` processing. Object `method_missing` We look for methods with '_per_' in their name and failing that, we try to use the name of the method to create a value with units. Otherwise the the processing goes to the prior, overriden `method_missing` processing.

Wait just a second. That's it?

If we're not using units we just take a hit when the methods we're using aren't defined and we have to drop into `method_missing` - and these sections fail quickly because we've assumed we're not working with units so the missing methods will have non-units sorts of names, right? That's not so bad. After all, if the control flow of the program goes through `method_missing`, then we should expect it may take some extra time to get to the real guts of the computations. What we're seeing is that units put a nearly insignificant load on the related objects when we're not using them. That's very good news.

Well, there is a little more, but it's a startup issue. We still may be feeding a big set of units to the system to "prime the units pump" so to speak, establishing the baseline units into our Ruby program. But of course, we haven't defined this yet, and we can even make the baseline unit loading be optional.

No, it really looks like the hit is small and that most of the time, working with unitless values will happily bypass our units system.

#### Defering Unit Application

As I alluded to in the beginning of this excursion into the creation of a units infrastructure, down through the years programmers have avoided explicitly using units in their code. Why? Well, besides the fact that they've perhaps only been explicitly available in a handful of languages (and after a quick survey, I still can't find anything like this in the mainstream) the explicit use of units is slower than just using numbers directly. To this I concede, absolutely! Look at all the additional definitions and lookups and alignments and extensions we've had to work our way through! Making units relatively transparent has not been easy, and there certainly is a performance penalty when using them.

But software developers are sharp thinkers. Disregarding units until values are ready to be output is still a good idea in many situations. By doing this you keep the numeric calculations running fast. Sure, errors can creep in - insidious ones that are hard to track down - but once everything's tested and certified, writing code this way should work fine.

The units package we've developed doesn't keep us from doing this, of course. We can unite numerics and units at any time during the process - and at any time we can pull out the numeric part of a NumericWithUnits and use it in any way we'd like. Given our ability to directly use scaling conversion factors like

centimeters_per_kilometer
seconds_per_day

or apply transforming ones like

x.miles_per_gallon
y.kV_per_meter

we can apply units late in the process and keep our numeric processing clean and fast. Just keep in mind that the debugging of complex unitless code isn't necessarily easy. I've been there.

I've transposed digits or slipped powers-of-ten and been off on a conversion factor more times than I can count. While this has (thankfully) always been caught during testing, it still has slowed down project deliveries. If you've ever been up late wondering why the heck the values aren't coming out right only to find you slipped up doing a conversion and it's been carried through a few dozen different transformations that have magnified the error to the point where the result bears no resemblance to what the expected, you know exactly what I'm talking about. And if you haven't, consider yourself lucky. Your only recourse is to become very good at copying 9-digit numbers from the back pages of reference books.

The bottom line is that if you're going to be doing a lot of mathematical calculations, sometimes it makes sense to strip the units off your values before and re-unite them afterwards. For instance, if you're going to multiply a bunch of big matrices together, you know the units of the values you're putting in and you know what the resulting units will be, strip the units first and reapply them when you finish - it'll run faster. But just be careful - one unitless number looks like any other. If you're sloppy, you'll be in trouble.

### The Pipe Dream - Interlude - Defining Units and Taking Stock

Okay. Before we go further, I need to come up for air to try a few things and see where we are so far.

We defined a lot of framework and wrote a lot of unit tests along the way. Everything we've built passes muster, but is everything we need there? Do we need a course correction? One way to find out... let's try to define some units for keeps, the kind of thing we'll want to come along for free when we write unit-sensitive Ruby code.

 definitions.rb   a units pipe dream ```Units.create :length do |m| m.system :english do |s| s.unit :name => :inch, :plural => :inches, :abbrev => :in s.unit :name => :foot, :plural => :feet, :equals => 12.inches, :abbrev => :ft s.unit :name => :yard, :equals => 3.feet, :abbrev => :yd s.unit :name => :mile, :equals => 5280.feet, :abbrev => :mi s.unit :name => :nautical_mile, :equals => 1852.meters, :abbrev => :nmi s.format :name => :feet_inches_and_32s, :format => lambda { |u| ru = u.round_to_nearest(1.inch,32) feet = ru.feet.floor inches = (ru-feet).inches "#{feet} #{inches.with_fraction 32}" } end m.system :metric do |s| s.unit :name => :meter, :equals =>39.37.inches, :abbrev => :m, :greek => :ten s.unit :name => :angstrom, :equals => 0.1.nanometers, :abbrev => :A end m.system :old_english do |s| s.unit :name => :fathom, :equals => 2.yards s.unit :name => :chain, :equals => 22.yards s.unit :name => :furlong, :equals => 660.feet s.unit :name => :league, :equals => 3.nmi end m.system :astronomical do |s| s.unit :name => :astronomical_unit, :equals => 149598000.kilometers, :abbrev => :AU s.unit :name => :light_year, :equals => speed_of_light*seconds_per_year, :abbrev => :ly s.unit :name => :parsec, :equals => 3.262.light_years, :abbrev => :pc end end ```

This is a decent set to go with, english system units, metric units with the greek prefixes and angstroms, some old archaic english units for fun, and some huge-distance astronomical units. Let's wire it in, run a unit test and... damn.

We blew up because we referenced the speed of light and subsquently light year before they were defined. When we added our forward reference mechanism to the defining state, we did Numeric - not an arbitrary Object. Apparently we need to discriminate between Numerics and other Objects, since we may have objects coming down the pipe. We need to expand on how Object handles missing units.

 units.rb   a units pipe dream ```class Object def method_missing(method,*args) begin s = method.to_s.split('_') if s.select{|e| e == 'per'}.size > 0 per_ratio method, args else convert_unit_value method, args end rescue Exception => exception method_missing_before_units method, args end end def convert_unit_value(method,*args) if Units.defining? reference = Units.make_forward_reference(method,Units.defining?) begin value = Units.convert 1, method Units.release_forward_reference reference value rescue MissingUnitsException Units.hold_forward_reference rescue UnitsException => exception units_problem("definition",exception,method,args) rescue Exception method_missing_before_units(method,args) end else begin Units.convert 1, method rescue UnitsException => exception units_problem("use",exception,method,args) rescue Exception method_missing_before_units(method,args) end end end def per_ratio(method,*args) if Units.defining? reference = Units.make_forward_reference(method,Units.defining?) puts "trying #{method} in #{Units.defining?}" begin value = convert_per_ratio method Units.release_forward_reference reference value rescue MissingUnitsException Units.hold_forward_reference rescue UnitsException => exception units_problem("definition",exception,method,args) rescue Exception method_missing_before_units(method,args) end else convert_per_ratio method end end def convert_per_ratio(method) ps = method.to_s.select{|e| e != 'and'}.join('_').split '_per_' raise UnitsException('invalid per method') if ps.size != 2 value = (1.unite ps.split('_')) / (1.unite(ps.split('_'))) raise UnitsException.new("per ratio has units") if value.kind_of? NumericWithUnits value end def units_problem(state,exception,method,args) raise exception end end ```

That takes care of that. We'll save forward references when we're defining units that resolve to Object's method_missing. So now, let's keep going. Let's take care of the time-based entries.

 definitions.rb   a units pipe dream ```Units.create :time do |m| m.system :base do |s| s.unit :name => :nanosecond, :equals => 0.000000001.seconds, :abbrev => [:ns, :nsec] s.unit :name => :microsecond, :equals => 0.000001.seconds, :abbrev => [:us, :usec] s.unit :name => :millisecond, :equals => 0.001.seconds, :abbrev => [:ms, :msec] s.unit :name => :second, :abbrev => [:s, :sec] s.unit :name => :minute, :equals => 60.seconds, :abbrev => [:m, :min] s.unit :name => :hour, :equals => 60.minutes, :abbrev => [:h, :hr] s.unit :name => :day, :equals => 24.hours, :abbrev => [:d, :dy] s.unit :name => :week, :equals => 7.days, :abbrev => [:w, :wk] end m.system :common do |s| s.unit :name => :month, :equals => [30.days, 4.weeks], :abbrev => [:m, :mo] s.unit :name => :year, :equals => [365.days, 12.months, 52.weeks], :abbrev => [:y, :yr] end m.system :long do |s| s.unit :name => :decade, :equals => 10.years s.unit :name => :century, :plural => :centuries, :equals => 100.years s.unit :name => :millenium, :plural => :millenia, :equals => 1000.years end m.system :old_english do |s| s.unit :name => :fortnight, :equals => 2.weeks end end ```

We once again wire, run, and... damn. Why did we blow up this time?

It's because of the way we defined the month and year. Look at how funky the equals element are - they're Arrays, which are definitely not handled correctly anywhere in the code we've written so far! What we want to say here is that a month is 30 days, or alternately 4 weeks and that a year is 12 months, or 365 days, or 52 weeks. We have to define both equalities in multiple ways since they can be valid in different contexts. Time conversion, so near and dear to us, has some human-based inexactness baked in, and we must handle it correctly.

Okay then, how do we reconcile this split-personality equality? We handle the inexactness by processing equality Arrays specially and throw in a bit of convention enacted through our simple_align method - but we have to establish some new framework first.

 units.rb   a units pipe dream ```class NumericWithUnits attr_accessor :numeric, :unit, :original def initialize(numeric,unit,power=1,original=nil) @numeric, @unit, @original = numeric, units_hash(unit)**power, original end end class Units def Units.convert(numeric,unit_identifier) if (candidates = lookup(unit_identifier)).size == 0 raise MissingUnitsException.new(unit_identifier.to_s) elsif !defining? if candidates.size > 1 raise AmbiguousUnitsException.new(unit_identifier.to_s) else unit = candidates NumericWithUnits.new(numeric,unit) end else if candidates.size == 1 units = candidates else units = candidates.select { |candidate| @@defining == candidate.units_system.units_measure } units = candidates.select { |candidate| @@defining.derived[candidate.units_measure] } if units.size == 0 end case units.size when 0 then raise MissingUnitsException.new(unit_identifier.to_s) when 1 then unit = units if unit.equals.kind_of? Array element = unit.equals NumericWithUnits.new(numeric*element.numeric,element.unit, 1,numeric.unite(unit)) else NumericWithUnits.new(numeric*unit.equals.numeric,unit.equals.unit, 1,numeric.unite(unit)) end else raise AmbiguousUnitsException.new(unit_identifier.to_s) end end end end class NumericWithUnits def promote_original @numeric, @unit = original.numeric, original.unit end end class UnitsUnit def normalize raise UnitsException.new("UnitUnits must have a name attribute") unless self.name self.name = self.name.to_s add_plural add_abbrevs add_equals equals.each { |n| n.promote_original } if equals.kind_of? Array self end end class NumericWithUnits def simple_align(target,all=true) factor = 1 target_unit = UnitsHash.new unit.each do |tu,tv| su = target.unit.keys.select {|u| u.units_measure.equal? tu.units_measure } if tu.equals.kind_of? Array m = tu.equals.select{|u| u.unit[su]} m = tu.equals.collect{|u| u.align(1.unite(su))}.compact if m.size == 0 factor *= (m.numeric)**tv target_unit[su] = tv else if su.size == 1 e = su.equals if e.kind_of? Array m = e.select{|u| u.unit[tu]} m = e.equals.collect{|u| u.align(1.unite(su))}.compact if m.size == 0 factor *= (1.0/m.numeric)**tv else factor *= (1.0*tu.equals.numeric/e.numeric)**tv end target_unit[su] = tv else target_unit[tu] = tv end end end raise UnitsException.new("units mismatch") if all && (target.unit != target_unit) NumericWithUnits.new(numeric*factor,target_unit) end end ```

The first realization is that because we have an inexactness, we need to retain it. If we work through it and leave it behind, we'll never know what the original intention was. So, we add a placeholder for the original intentions in a NumericWithUnits, and we tuck away the original values as we're defining units. Once we've defined the unit and are normalizing it, we add a step that says "if we have multiple interpretations, use the original intentions rather than the computed value" so we can figure out which one to use later when we have more context. We'll set everything up to make use of the intentions during conversion.

The heart of the mechanism is buried in alignment. What we do here is make it sensitive to units with arrays embeded into their notion of equality. When we have to align that involves a unit with inexact equality, we first check to see if a member of the equality contains a NumericWithUnits containing the unit we're after; if so we use it. If the unit isn't there, we then take each element of the equality and try to convert to the target unit - the first one we find wins and is rolled up into the conversion.

Here's where the subtle use of convention comes in: elements in the equality Array are tried in order. The first elements are preferred over the latter elements. This tightens up the inexactness and makes conversions predictable. Of course, there's nothing preventing us from using later elements in our conversions - we just have to convert through the Array explicitly.

But perhaps we've been to generic. Usually inexactness like this is caused by trying too hard to model everything with artificial rules rather than include the outlying richness of the real world. For instance, in business planning a generic month is typically considered to be 30 days long - but it's perfectly reasonable to ask how many days there are in August and use that as a basis for conversion. It's the facts about the real world that aren't reflected in our units system.

We can push on our units system slightly and include some of the real-world exceptions to the rules. For instance:

 definitions.rb   a units pipe dream ```Units.create :time do |m| m.system :months do |s| s.unit :name => :january, :equals => 31.days s.unit :name => :february, :equals => [28.days, 29.days] s.unit :name => :march, :equals => 31.days s.unit :name => :april, :equals => 30.days s.unit :name => :may, :equals => 31.days s.unit :name => :june, :equals => 30.days s.unit :name => :july, :equals => 31.days s.unit :name => :august, :equals => 31.days s.unit :name => :september, :equals => 30.days s.unit :name => :october, :equals => 31.days s.unit :name => :november, :equals => 30.days s.unit :name => :december, :equals => 31.days end end ```

And spin a little bit of code into Object's method_missing method:

 units.rb   a units pipe dream ```class Object def method_missing(method,*args) begin s = method.to_s.split('_') if s.select{|e| e == 'per'}.size > 0 per_ratio method, args elsif s.select{|e| e == 'in'}.size > 0 per_ratio method.to_s.sub(/_in_/,'_per_').to_sym, args else convert_unit_value method, args end rescue Exception => exception method_missing_before_units method, args end end end ```

This lets us do wonderful things like

 puts hours_in_january ==> '744.0' puts july.weeks ==> '4.42857142857143 weeks'

Yeah, that's just the way I'd expect it to read. Except that fractional weeks aren't natural - when I ask for weeks, I want weeks and days. Let's add one of our formatters and tame this a little.

 definitions.rb   a units pipe dream ```Units.create :time do |m| m.system :base do |s| s.format :name => :weeks_and_days, :format => lambda { |u| weeks = u.weeks.floor days = (u-weeks).days.round weeks == 0 ? (days == 0 ? "#{weeks}" : "#{days}") : "#{weeks} #{days}" } end end ```

Now we can do

 puts july.format :weeks_and_days ==> '4 weeks 3 days'

Beautiful! Except, darn it, using that format method just seems to get in our way. Sure, it's specific and we really do need to say what format we should use, but couldn't we just call it from the to_s method? Of course!

 units.rb   a units pipe dream ```class NumericWithUnits def to_s(format = nil) format == nil ? "#{numeric} #{unit.to_s(numeric)}" : self.format(format) end end ```

That should do it.

 puts july.to_s :weeks_and_days ==> '4 weeks 3 days'

I just love Ruby!

Okay. So now, let's get back to the speed of light.

 definitions.rb   a units pipe dream ```Units.derive :velocity, Units.length/Units.time do |m| m.system :metric do |s| s.unit :constant => true, :name => :speed_of_light, :no_plural => true, :equals => 299792458.meters/second, :abbrev => :c end end ```

We see a few interesting things here, namely the use of :constant and :no_plural, but let's ignore those for a second. The big thing is that this definition will work, and should resolve our issue with the undefined forward references. Let's make sure.

 puts 4.5.ly.in_mi ==> '26435654658917.8 miles' puts 4.5.ly.AU ==> '284389.813364457 astronomical_units' puts speed_of_light.miles ==> '186282.024486427 mi / s' puts speed_of_light.feet_nanosecond ==> '0.983569089299333 ft / ns'

Yep, Grace Hopper was right - about a foot per nanosecond.

One thing troubles me though. We've used underscores in the names of light years and astronomical units. I want to clean up this output.

 units.rb   a units pipe dream ```class UnitsHash def to_s(numeric = 1,use_abbrevs = false) if size == 1 && (su = select {|k,v| v == 1}).size == 1 && !use_abbrevs su = su.to_a (numeric == 1 ? su.name : su.plural).gsub(/_/,' ') else abbrevs_to_s end end end ```

Now let's go back and try that printing again.

 puts 4.5.ly.AU ==> '284389.813364457 astronomical units'

No underscore, much nicer. Picky, perhaps. But it's the details that make it smooth. Looking at it symetrically however, what about underscores on the input side?

 puts 4.5.light_years.AU ==>

Error. Damn, it tried to parse light_years into converting light and years. This is a tough problem, one based in the ambiguity of human utterance. We want things both ways - in some cases the units should be separate, in the other we want the units divided. If we don't do it right, our units-users will get very confused. And what if it occurs during definition versus happening when units are being used? We need a plan.

Well, it looks like we have it right already during definition. If we look back to the definition of parsec in the length measure, that's defined as 3.262 light years, and it works just fine. The key there is that we don't decompose the units on underscores - we just take them verbatim as a unit identifier. Great and whew. Half our work is already working right and we don't have to try to handle some really hairy forward referencing issues. So let's look at the half not done.

 puts 4.5.unite("light_years").AU ==> '284389.813364457 astronomical units' puts 4.5.unite(["light_years"]).AU ==> '284389.813364457 astronomical units'

Ah ha! There is hope! If we use unite, we can get around our issue by avoiding the parsing! And we could give unite an Array of units if we have multiples! Therefore, our plan is to convert these underscored units into the elements of an array during the parse before we hand them to the unite. Hmmm. But not just unite. We need to be underscore-sensitive anywhere we parse a method as units. It's time to pick a convention and do a little software dance.

Here's what we'll do. We'll look for occurences of the string `'_and_'`. If we see any, we'll automatically assume we have units with embeded underscores between them. If we don't see any, we'll try the string by itself. If we get back a MissingUnitsException, we'll assume the underscores are separating units and we'll parse out each of them. We'll be careful to only do our decomposition during unit use - we'll leave unit definition alone.

 units.rb   a units pipe dream ```class Object def convert_per_ratio(method) ps = method.to_s.split '_per_' raise UnitsException('invalid per method') if ps.size != 2 value = (1.unite ps)/(1.unite ps) raise UnitsException.new("per ratio has units") if value.kind_of? NumericWithUnits value end end class Numeric def method_missing(method,*args) if Units.defining? reference = Units.make_forward_reference(method,Units.defining?) begin value = Units.convert self, method Units.release_forward_reference reference value rescue MissingUnitsException Units.hold_forward_reference rescue UnitsException => exception units_problem("definition",exception,method,args) rescue Exception => exception method_missing_before_units(method,args) end else begin ps = method.to_s.split '_per_' case ps.size when 1 then unite method when 2 then unite(ps)/(1.unite(ps)) else raise UnitsException('invalid per method') end rescue UnitsException => exception units_problem("usage",exception,method,args) rescue Exception method_missing_before_units(method,*args) end end end def unite(unit=nil,power=1,measure=nil) if !unit self else if (unit.kind_of? Array) units = UnitsHash.new unit.each {|u| units.merge! 1.unite(u)} unit = units elsif (unit.kind_of? String)||(unit.kind_of? Symbol) s = unit.to_s as = s.split('_and_') if as.size == 1 units = Units.lookup(as) case units.size when 1 then unit = units when 0 as = as.split('_') units_problem("usage", AmbiguousUnitsException.new(unit), :unit=,unit) if as.size == 0 unite(as,power,measure).unit else units_problem("usage", AmbiguousUnitsException.new(unit), :unit=,unit) end else unite(as,power,measure).unit end elsif unit.kind_of? NumericWithUnits unit = unit.unit end NumericWithUnits.new(self,unit,power) end end end class NumericWithUnits def method_missing(method,*args) begin s = method.to_s ms = s.split '_' if ms == 'to' convert! s.gsub(/^to_/,"") elsif ms == 'in' convert s.gsub(/^in_/,"") elsif ms.select{|e| e == 'per'}.size > 0 convert_per method else convert s end rescue Exception value = numeric.send(method,*args) if (value.kind_of? String) "#{value} #{unit.to_s(numeric)}" else NumericWithUnits.new(value,unit) end end end def convert_per method ps = method.to_s.split '_per_' raise UnitsException.new('invalid per method') if ps.size != 2 numerator = 1.unite(ps) numerator_units = Set.new numerator.unit.keys denominator = 1.unite(ps) denominator_units = Set.new denominator.unit.keys positives = unit.keys.collect{|k| unit[k] > 0 ? k : nil}.compact! negatives = unit.keys.collect{|k| unit[k] < 0 ? k : nil}.compact! numerator_positives = Set.new 1.unite(positives).align(numerator,false).unit.keys numerator_negatives = Set.new 1.unite(negatives).align(numerator,false).unit.keys denominator_positives = Set.new 1.unite(positives).align(denominator,false).unit.keys denominator_negatives = Set.new 1.unite(negatives).align(denominator,false).unit.keys if (numerator_units == numerator_positives) && (denominator_units == denominator_negatives) convert(ps+'_and_'+ps) elsif (numerator_units == numerator_negatives) && (denominator_units == denominator_positives) convert(ps+'_and_'+ps)**-1 else raise UnitsException.new('invalid per units') end end end ```

Apart from determining if we need special treatment for the per conversion, we've pulled all of the heavy lifting into the unite method. Now we can say things like

 puts 4.5.light_years.astronomical_units ==> '284389.813364457 astronomical units'

and they just work.

Okay, let's quick jump back to the :constant and :no_plural we used to define the speed of light. We skipped over them because they really don't mean much - the :constant is just for information purposes, and the :no_plural is just a cue for default formating. We're going to leave the :constant out of everything - remember, since a UnitsUnit is a kind of MethodicHash, it's just getting added as a hashed value with no other overhead. We'll add just a little bit of code to take care of the :no_plural case.

 units.rb   a units pipe dream ```class UnitsUnit def add_plural self.plural = (self.no_plural == true) ? self.name : (plural = self.plural) ? plural.to_s : self.name+'s' end end ```

That's it. If it's set to no plural, we make the plural the same as the singular name. This allows fictitios statements to be made, like

 puts 4.5.speed_of_light ==> '4.5 speed of light'

and keep the unit name singular even when a plural would have been used. The trick is that we are using the plural, but it's the same as the singular form.

### Suddenly, Rousted From the Dream

A good friend of mine recently mentioned the need for a Units Framework - and I couldn't help but committing the gem to the waking hours before I had a chance to complete the work. However, since the scalar are complete, now's a good time. More is to come, but for now I've been rousted.

I will be back to bed shortly, hopefully to pick up where I've left off...