<div>On Fri, Mar 18, 2011 at 5:02 AM, Magnus Therning <span dir="ltr"><<a href="mailto:magnus@therning.org" target="_blank">magnus@therning.org</a>></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 <<a href="mailto:amindfv@gmail.com" target="_blank">amindfv@gmail.com</a>> wrote:<br>> Hi All,<br>> Is there a good way to easily convert between units?<br>
> For example, let's say I have a data type:<br>> data Volumes = Teaspoon | Tablespoon | Slice | FluidOunces<br>> ... and I want to define an infix function '<+>', which adds together<br>
> amounts of food:<br>> (1, Slice, cake) <+> (1, Volume Tablespoon, sugar) <+> (32, Volume<br>> FluidOunces, almondMilk)<br>> Which would return:<br>> (3200, Teaspoons)<br>> What is the most efficient way to define equivalency/conversion between<br>
> these measures?<br>> I remember an interesting method for celsius-farenheit conversion in<br>> Higher-Order Perl, using function composition, but that was between only 2<br>> units. I'd like something where I don'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 -> 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'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 "meeting points" for the units.</div><div>So, we say that if we'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 "chain" of conversions.</div>
<div>I'd then "convert down" 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 "step-down"s and "step-up"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 -> 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 "step" as far down as needed:</div>
<div><br></div><div><div>convertDown :: Measure -> Volume -> 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'd have to find the smaller unit, convert down, perform the math and then do a "convert up" operation.</div>
<div><br></div><div>An added benefit to this method is that defining a unit smaller than the current smallest wouldn't involve revising the "base unit" measures.</div>
<div><br></div><div>This seems like a pretty common C.S. problem; I'm surprised I haven'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>