Created
March 2, 2013 19:24
-
-
Save sw17ch/5072741 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> 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