module Data.Distance
  ( Distance
  , meters, centimeters, millimeters, kilometers
  , toMeters, toCentimeters, toMillimeters, toKilometers
  )
where

-- | An abstract distance. Use the provided smart constructors to create
-- a meaningful distance. Note that on first sight a `Num` instance might
-- seem desirable, but this would defeat the purpose of having transparent
-- and explicitly constructed distances due to `fromInteger`.
newtype Distance
  = Distance { unDistance :: Double } -- as meters
  deriving (Show, Eq, Ord)

-- | An empty `Distance` is 0, and `mappend` is defined as addition
instance Monoid Distance where
    mempty = Distance 0
    mappend (Distance a) (Distance b) = Distance (a + b)

meters :: Double -> Distance
meters = Distance

centimeters :: Double -> Distance
centimeters = meters . (/ 100)

millimeters :: Double -> Distance
millimeters = centimeters . (/ 10)

kilometers :: Double -> Distance
kilometers = meters . (* 1000)

toMeters :: Distance -> Double
toMeters = unDistance

toCentimeters :: Distance -> Double
toCentimeters = (*100) . toMeters

toMillimeters :: Distance -> Double
toMillimeters = (*10) . toCentimeters

toKilometers :: Distance -> Double
toKilometers = (/1000) . toMeters