Darren
Darren

Reputation: 2938

Is there a Haskell equivalent of F# units of measure?

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

Answers (6)

George
George

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

Prasanna K Rao
Prasanna K Rao

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

marbu
marbu

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

mb14
mb14

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

Doug McClean
Doug McClean

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

Peter Hall
Peter Hall

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

Related Questions