Skip to content

Instantly share code, notes, and snippets.

@sw17ch
Created March 2, 2013 19:24
Show Gist options
  • Save sw17ch/5072741 to your computer and use it in GitHub Desktop.
Save sw17ch/5072741 to your computer and use it in GitHub Desktop.
> module Units where
The goal of this module is to demonstrate some basic unit conversion and
manipulation using the Haskell type system.
First, let's define our data types. We'll represent things as Doubles for
convenience.
> newtype Meters = Meters Double deriving (Show)
> newtype Feet = Feet Double deriving (Show)
In order to do the actual calculations, we'll write a typeclass that converts
the given type to Meters. We'll also implement an interface that allows us to
make our given type from Meters.
> class Distance a where
> toMeters :: a -> Meters
> fromMeters :: Meters -> a
Now that we've defined a Distance typeclass, we need to describe how Meters and
Feet implement Distance.
The Distance instance for Meters is just `id` in both cases.
> instance Distance Meters where
> toMeters = id
> fromMeters = id
The Distance instance for Feet is depends on the conversion between Feet and
meters.
> feetPerMeter :: Double
> feetPerMeter = 3.28084
> instance Distance Feet where
> toMeters (Feet d) = Meters $ d / feetPerMeter
> fromMeters (Meters d) = Feet $ d * feetPerMeter
Before we get going with arithmetic definitions, let's abstract out the
"convert to meters, do math, convert back" concept.
> asMeters :: (Distance a, Distance b) => (Double -> Double -> Double) -> a -> b -> a
> asMeters f a b = let (Meters ma) = toMeters a
> (Meters mb) = toMeters b
> in fromMeters $ Meters (ma `f` mb)
There's some goofy properties of the Num typeclass, so we're making our own
functions to perform arithmetic.
> (<<+>>) :: (Distance a, Distance b) => a -> b -> a
> a <<+>> b = asMeters (+) a b
> (<<->>) :: (Distance a, Distance b) => a -> b -> a
> a <<->> b = asMeters (-) a b
> (<<*>>) :: (Distance a, Distance b) => a -> b -> a
> a <<*>> b = asMeters (*) a b
> (<</>>) :: (Distance a, Distance b) => a -> b -> a
> a <</>> b = asMeters (/) a b
Here's a function that defines a few examples.
> examples :: IO ()
> examples = do
> putStrLn "> Meters 1"
> print $ Meters 1
> putStrLn "> Feet 1"
> print $ Feet 1
> putStrLn "> toMeters (Feet 1)"
> print $ toMeters (Feet 1)
> putStrLn "> fromMeters (Meters 1) :: Feet"
> print $ (fromMeters (Meters 1) :: Feet)
> putStrLn "> Meters 1 <<+>> Feet 1"
> print $ Meters 1 <<+>> Feet 1
> putStrLn "> Feet 1 <<+>> Meters 1"
> print $ Feet 1 <<+>> Meters 1
Here's the output from the example function:
> Meters 1
Meters 1.0
> Feet 1
Feet 1.0
> toMeters (Feet 1)
Meters 0.3047999902464003
> fromMeters (Meters 1) :: Feet
Feet 3.28084
> Meters 1 <<+>> Feet 1
Meters 1.3047999902464003
> Feet 1 <<+>> Meters 1
Feet 4.2808399999999995
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment