Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

haskell

f#

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? :)

like image 216
Darren Avatar asked Jun 11 '13 13:06

Darren


People also ask

How do you define a function in Haskell?

The most basic way of defining a function in Haskell is to ``declare'' what it does. For example, we can write: double :: Int -> Int double n = 2*n. Here, the first line specifies the type of the function and the second line tells us how the output of double depends on its input.

What is function composition in Haskell?

Composing functions is a common and useful way to create new functions in Haskell. Haskell composition is based on the idea of function composition in mathematics. In mathematics, if you have two functions f(x) and g(x), you compute their composition as f(g(x)). The expression f(g(x)) first calls g and then calls f.


2 Answers

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
like image 99
Peter Hall Avatar answered Sep 17 '22 15:09

Peter Hall


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.

like image 20
Doug McClean Avatar answered Sep 17 '22 15:09

Doug McClean