Reputation: 2938
F# has a units of measure capability, described at http://msdn.microsoft.com/en-us/library/dd233243.aspx as follows:
[<Measure>] type unit-name [ = measure ]
This allows units to be defined such as:
type [<Measure>] USD
type [<Measure>] EUR
And code to be written as:
let dollars = 25.0<USD>
let euros = 25.0<EUR>
// Results in an error as the units differ
if dollars > euros then printfn "Greater!"
It also handles conversions (I'm guessing that means Measure has some functions defined that let Measures be multiplied, divided and exponentiated):
// Mass, grams.
[<Measure>] type g
// Mass, kilograms.
[<Measure>] type kg
let gramsPerKilogram: float<g kg^-1> = 1000.0<g/kg>
let convertGramsToKilograms (x: float<g>) = x / gramsPerKilogram
My instincts tell me it should be possible to implement a similar capability in Haskell, but I've not been able to find any examples of how to do it.
Edit: oh my word it's a huge can of worms! There's a research paper at http://research.microsoft.com/en-us/um/people/akenn/units/CEFP09TypesForUnitsOfMeasure.pdf. I'm guessing it's more than a few lines of code to implement the whole thing. Summer project anyone? :)
Upvotes: 15
Views: 1972
Reputation: 2801
I don't know Haskell much though I am interested in learning and am still junior with F#, however, it seems a general solution should be around treating measures as a tuple of scalar and a label representing the unit, (N,M).
For example, let measure be a generic tuple of measure<N,M>
, where N
is a number and M
is a string
, then =, +, -, *, /, ^
can be overloaded to implement evaluation for given measures, a, b, and c
, scalar p
, boolean k, and measure label normalizing function norm
such that
a = b = (Na,Ma) = (Nb,Mb) = (Na = Nb) and norm(Ma) = norm(Mb) = k
a + b = (Na,Ma) + (Nb,Mb) = (Na + Nb, Ma) = c provided norm(Ma) = norm(Mb)
a * b = (Na,Ma) * (Nb,Mb) = (Na * Nb, Ma * Mb) = c
a ^ p = (Na,Ma) ^ p = (Na ^ p , Ma ^ p ) = c
....
Upvotes: 0
Reputation: 1086
OK. I know the question is too old. But, there is a GHC plugin build to provide unit of measure capability in Haskell. Please refer to this Adam Gundry's paper.
Upvotes: 4
Reputation: 2021
Check haskell.org wikipage about enforcing units in Haskell first:
https://wiki.haskell.org/Physical_units
There are multiple options but I'm not sure if there actually is something similar to "Units of Measure" you mentioned.
Upvotes: 1
Reputation: 22596
The units package seems promising and allows conversion between different unit of the same dimension. However, I haven't used it and I don't know if it's stable or not. In fairness, I tried to install it today and the installation failed ...
Upvotes: 1
Reputation: 14485
The dimensional and dimensional-tf (with type families instead of multi-parameter type classes) libraries are pretty nice and can handle most of the issues presented in your example.
I don't think the library lets you define custom dimensions like currencies, though. As far as I know you would need to modify the library code to do that.
Upvotes: 7
Reputation: 58735
Wrap numbers in a newtype and give them a Num
instance.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype GBP n = GBP n deriving (Show, Num, Eq, Ord)
newtype USD n = USD n deriving (Show, Num, Eq, Ord)
Usage:
ghci> let a1 = GBP 2
ghci> let a2 = GBP 5
ghci> a1 + a2
GBP 7
ghci> let b1 = USD 3
ghci> let b2 = USD 6
ghci> b1 + b2
USD 9
ghci> a1 + b2 -- should be an error for mixing currencies
<interactive>:8:6:
Couldn't match expected type `GBP Integer'
with actual type `USD Integer'
In the second argument of `(+)', namely `b2'
In the expression: a1 + b2
In an equation for `it': it = a1 + b2
Upvotes: 12