<div>On Fri, Mar 18, 2011 at 5:02 AM, Magnus Therning <span dir="ltr">&lt;<a href="mailto:magnus@therning.org" target="_blank">magnus@therning.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-width:1px;border-left-color:rgb(204, 204, 204);border-left-style:solid;padding-left:1ex">

<div><div></div><div>On Thu, Mar 17, 2011 at 21:17, Tom Murphy &lt;<a href="mailto:amindfv@gmail.com" target="_blank">amindfv@gmail.com</a>&gt; wrote:<br>&gt; Hi All,<br>&gt;      Is there a good way to easily convert between units?<br>

&gt;      For example, let&#39;s say I have a data type:<br>&gt;      data Volumes = Teaspoon | Tablespoon | Slice | FluidOunces<br>&gt;      ... and I want to define an infix function &#39;&lt;+&gt;&#39;, which adds together<br>

&gt; amounts of food:<br>&gt;      (1, Slice, cake) &lt;+&gt; (1, Volume Tablespoon, sugar) &lt;+&gt; (32, Volume<br>&gt; FluidOunces, almondMilk)<br>&gt;      Which would return:<br>&gt;      (3200, Teaspoons)<br>&gt;      What is the most efficient way to define equivalency/conversion between<br>

&gt; these measures?<br>&gt;      I remember an interesting method for celsius-farenheit conversion in<br>&gt; Higher-Order Perl, using function composition, but that was between only 2<br>&gt; units. I&#39;d like something where I don&#39;t have to provide n^2 definitions.<br>

<br></div></div>I wrote two blog posts on something that sounds related:<br><a href="http://therning.org/magnus/archives/354" target="_blank">http://therning.org/magnus/archives/354</a><br><a href="http://therning.org/magnus/archives/355" target="_blank">http://therning.org/magnus/archives/355</a><br>

<br>Maybe they could help.<br><br>/M<br><font color="#888888"><br>--<br>Magnus Therning                      OpenPGP: 0xAB4DFBA4<br>email: <a href="mailto:magnus@therning.org" target="_blank">magnus@therning.org</a>   jabber: <a href="mailto:magnus@therning.org" target="_blank">magnus@therning.org</a><br>

twitter: magthe               <a href="http://therning.org/magnus" target="_blank">http://therning.org/magnus</a></font></blockquote><div><br></div><div><br></div><div><br></div><div><br></div><div> </div></div><div><br>
</div>
<div>So given</div><div><div>data Measure = M Volume Float</div><div>  deriving (Show, Eq)</div><div>data Volume = Slice </div><div>            | FluidOunce </div><div>            | Tablespoon</div><div>            | Teaspoon</div>
<div>  deriving (Show, Eq)</div></div>

<div><br></div><div>Method one and two would be to convert all units to the smallest unit:</div><div>toTsp                           :: Measure -&gt; Measure</div><div>[...]</div><div><div>toTsp (M Slice a)          = M Teaspoon (a * 350)</div>
<div>toTsp (M FluidOunce a) = M Teaspoon (a * 34)</div></div>
<div>[...]</div><div><br></div><div>Perform the addition, then convert it to a more appropriate measure (back to Cake Slices...).</div><div>     It seems that there&#39;s a potential loss of precision, if I have to for example convert Megaliter measures to Teaspoons. </div>


<div><br></div><div><br></div><div>Method three involves using typeclasses to define common &quot;meeting points&quot; for the units.</div><div>So, we say that if we&#39;re adding Slices and FluidOunces, both measures should be converted to FluidOunces before the addition.</div>

<div>The problem there is that, given n measurement units, we have to declare n^2 typeclass instances by hand.</div><div><br></div><div><br></div><div>Another way that I can see to do this is to define a &quot;chain&quot; of conversions.</div>

<div>I&#39;d then &quot;convert down&quot; only to the smallest-common unit. </div><div><br></div><div>So I could define </div><div>(warning: extreme kludge ahead)</div><div>a series of &quot;step-down&quot;s and &quot;step-up&quot;s:</div>

<div><br></div><div>Given a largest-to-smallest ordering of Slice, FluidOunce, Tablespoon, Teaspoon, I can define:</div><div><br></div><div><div>stepDown                           :: Measure -&gt; Measure</div><div>stepDown (M Slice a)          = M FluidOunce (a * 120)</div>
<div>stepDown (M FluidOunce a) = M Tablespoon (a * 2)</div><div>stepUp   (M FluidOunce a)   = M Slice (a / 120)</div><div>stepUp   (M Tablespoon a)   = M FluidOunce (a / 2)</div></div><div>[...]</div><div><br></div><div><br>
</div><div>and then a function to &quot;step&quot; as far down as needed:</div>
<div><br></div><div><div>convertDown :: Measure -&gt; Volume -&gt; Measure</div><div>convertDown (M unit measure) goalUnit</div><div>                     | unit == goalUnit = M unit measure</div><div>                     | otherwise        = fun5 (stepDown (M unit measure))  goalUnit</div>
</div><div><br></div><div><br></div><div><br></div><div>And then if I wanted to add a Slice of cake to a Tablespoon of almond milk, I&#39;d have to find the smaller unit, convert down, perform the math and then do a &quot;convert up&quot; operation.</div>
<div><br></div><div>An added benefit to this method is that defining a unit smaller than the current smallest wouldn&#39;t involve revising the &quot;base unit&quot; measures.</div>
<div><br></div><div>This seems like a pretty common C.S. problem; I&#39;m surprised I haven&#39;t been able to find more resources about it.</div><div><br></div><div>Thanks for your time!</div><div><br></div><div>Tom</div>

<div>P.S. Thank you, Magnus, for those resources!</div>