packages feed

dimensional 1.0.0.0 → 1.0.1.0

raw patch · 9 files changed

+425/−275 lines, 9 filesdep ~basedep ~exact-pi

Dependency ranges changed: base, exact-pi

Files

+ CHANGELOG.md view
@@ -0,0 +1,90 @@+1.0.1.0 (2015-11)
+------------------
+* Added Numeric.Units.Dimensional.Coercion module.
+* Bumped exact-pi dependency to < 0.5.
+* Restored changelog.
+* Addressed issues with documentation.
+
+1.0.0.0 (2015-11)
+------------------
+* Changed to DataKinds and ClosedTypeFamilies encoding of dimensions.
+* Added names and exact values to `Unit`s.
+* Added `AnyUnit` and `AnyQuantity` for quantities whose dimension is statically unknown.
+* Added Storable and Unbox instances for `Quantity`.
+* Added dimensionally-polymorphic `siUnit` for the coherent SI base unit of any dimension.
+* Added some additional units.
+
+0.13.0.2 (2015-04)
+------------------
+*  Corrected definition of lumen.
+
+
+0.13.0.1 (2014-09)
+------------------
+*  Bumped time dependency to < 1.6.
+
+
+0.13 (2014-02)
+--------------
+*  Bump major version (should have been done in previous version).
+
+
+0.12.3 (2014-02)
+----------------
+*  Bump numtype dependency to 1.1 (GHC 7.8.1 compatibility fix).
+*  Added `Torque`.
+*  Added D.. for the type synonym quantities (e.g., `Angle`).
+
+
+0.12.2 (2013-11)
+----------------
+*  Added `FirstMassMoment`, `MomentOfInertia`, `AngularMomentum`.
+*  Improved unit numerics.
+
+
+0.12.1 (2013-07)
+----------------
+*  Typeable Dimensionals.
+
+
+0.12 (2013-06)
+--------------
+*  Polymorphic `_0` (closes issue 39).
+*  Added `astronomicalUnit`.
+*  Added imperial volume units.
+*  Added 'mil' (=inch/1000).
+*  Added [`tau`][3].
+*  Added `KinematicViscosity`.
+
+[3]: http://tauday.com/tau-manifesto
+
+
+0.10.1.2 (2011-09)
+------------------
+*  Bumped time dependency to < 1.5.
+
+
+0.10.1.2 (2011-08)
+------------------
+*  Bumped time dependency to < 1.4.
+
+
+0.10.1 (2011-08)
+------------------
+GHC 7.2.1 compatibility fix:
+
+*  Increased CGS context-stack to 30.
+
+
+0.10 (2011-05)
+-------------
+See the [announcement][2].
+
+[2]: http://flygdynamikern.blogspot.se/2011/05/announce-dimensional-010.html
+
+
+0.9 (2011-04)
+-------------
+See the [announcement][1].
+
+[1]: http://flygdynamikern.blogspot.se/2011/04/announce-dimensional-09.html
README.md view
@@ -1,5 +1,4 @@-dimensional
-==============
+# dimensional
 
 This library provides statically-checked dimensional arithmetic for physical quantities, using the 7 SI base dimensions.
 
@@ -9,8 +8,7 @@ [![Build Status](https://travis-ci.org/bjornbm/dimensional-dk.svg?branch=master)](https://travis-ci.org/bjornbm/dimensional-dk)
 [![Hackage Version](http://img.shields.io/hackage/v/dimensional.svg)](http://hackage.haskell.org/package/dimensional)
 
-Usage
------
+## Usage
 
 Simply importing `Numeric.Units.Dimensional.Prelude` provides access to dimensional arithmetic opertors, SI units and other common units
 accepted for use with the SI, and convenient aliases for quantities with commonly used dimensions.
@@ -46,8 +44,7 @@ differenceFromStandardValue = approximateAccelerationDueToGravityOnEarth /~ gee
 ```
 
-Contributing
-------------
+## Contributing
 
 For project information (issues, updates, wiki, examples) see:
   https://github.com/bjornbm/dimensional-dk
dimensional.cabal view
@@ -1,5 +1,5 @@ name:                dimensional
-version:             1.0.0.0
+version:             1.0.1.0
 license:             BSD3
 license-file:        LICENSE
 copyright:           Bjorn Buckwalter 2006-2015
@@ -32,6 +32,7 @@     Requires GHC 7.8 or later.
 
 extra-source-files:  README.md,
+                     CHANGELOG.md,
                      examples/README,
                      examples/GM.lhs
 
@@ -42,13 +43,14 @@ library
   build-depends:       base >= 4.7 && < 5,
                        deepseq >= 1.3,
-                       exact-pi >= 0.2.1.1 && < 0.3,
+                       exact-pi >= 0.2.1.1 && < 0.5,
                        numtype-dk >= 0.5 && < 1.1,
                        vector >= 0.10
   hs-source-dirs:      src
   default-language:    Haskell2010
   ghc-options:         -Wall
   exposed-modules:     Numeric.Units.Dimensional,
+                       Numeric.Units.Dimensional.Coercion,
                        Numeric.Units.Dimensional.Prelude,
                        Numeric.Units.Dimensional.Quantities,
                        Numeric.Units.Dimensional.SIUnits,
@@ -61,7 +63,8 @@                        Numeric.Units.Dimensional.UnitNames,
                        Numeric.Units.Dimensional.UnitNames.InterchangeNames,
                        Numeric.Units.Dimensional.Variants
-  other-modules:       Numeric.Units.Dimensional.UnitNames.Internal
+  other-modules:       Numeric.Units.Dimensional.Internal,
+                       Numeric.Units.Dimensional.UnitNames.Internal
 
 test-suite tests
   type:                exitcode-stdio-1.0
src/Numeric/Units/Dimensional.hs view
@@ -117,22 +117,28 @@ in its error messages.
 
 >>> x = 1 *~ meter + 1 *~ second
-Couldn't match expected type `Pos1' against inferred type `Zero'
-    Expected type: Unit DLength t
-    Inferred type: Unit DTime a
+Couldn't match type 'Numeric.NumType.DK.Integers.Zero
+               with 'Numeric.NumType.DK.Integers.Pos1
+  Expected type: Unit 'Metric DLength a
+    Actual type: Unit 'Metric DTime a
   In the second argument of `(*~)', namely `second'
   In the second argument of `(+)', namely `1 *~ second'
 
 In other cases the error messages aren't very friendly.
 
 >>> x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram
-Couldn't match expected type `Zero'
-    against inferred type `Neg Zero'
-  When using functional dependencies to combine
-    Sub Zero (Pos Zero) (Neg Zero),
-      arising from use of `/' at ...
-    Sub Zero (Pos Zero) Zero,
-      arising from use of `/' at ...
+Couldn't match type 'Numeric.NumType.DK.Integers.Zero
+               with 'Numeric.NumType.DK.Integers.Neg1
+  Expected type: Quantity DMass a
+    Actual type: Dimensional
+                   ('Numeric.Units.Dimensional.Variants.DQuantity
+                    Numeric.Units.Dimensional.Variants.* 'Numeric.Units.Dimensional.Variants.DQuantity)
+                   (DLength / DTime)
+                   a
+  In the first argument of `(+)', namely `1 *~ meter / (1 *~ second)'
+  In the expression: 1 *~ meter / (1 *~ second) + 1 *~ kilo gram
+  In an equation for `x':
+      x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram
 
 It is the author's experience that the usefullness of the compiler
 error messages is more often than not limited to pinpointing the
@@ -149,18 +155,6 @@ There are also plenty of elementary functions to add. The 'Floating'
 class can be used as reference.
 
-Another useful addition would be decent 'Show' and 'Read' instances.
-The 'show' implementation could output the numerical value and the
-unit expressed in (base?) SI units, along the lines of:
-
-> instance (Fractional a, Show a) => Show (Length a)
->   where show x = show (x /~ meter) ++ " m"
-
-Additional functions could be provided for "showing" with any unit
-and prefix.  The 'read' implementation should be able to read values
-with any unit and prefix. It is not clear to the author how to best
-implement these.
-
 Additional physics models could be implemented. See <#note3 [3]> for ideas.
 
 == Related work
@@ -238,9 +232,9 @@   where
 
 import Prelude
-  ( Show, Eq(..), Ord, Bounded(..), Num, Fractional, Floating, Real, RealFloat, Functor, fmap
-  , (.), flip, show, (++), fromIntegral, fromInteger, fromRational, error, max, succ
-  , Int, Integer, Integral, ($), uncurry, realToFrac, otherwise, undefined, String
+  ( Eq(..), Num, Fractional, Floating, Real, RealFloat, Functor, fmap
+  , (.), flip, (++), fromIntegral, fromInteger, fromRational, error, max, succ
+  , Int, Integer, Integral, ($), uncurry, realToFrac, otherwise
   )
 import qualified Prelude
 import Numeric.NumType.DK.Integers
@@ -248,28 +242,17 @@   , pos2, pos3
   , KnownTypeInt, toNum
   )
-import Control.Applicative
-import Control.DeepSeq
-import Control.Monad (liftM)
-import Data.Coerce (coerce)
 import Data.Data
 import Data.ExactPi
 import Data.Foldable (Foldable(foldr, foldl'))
 import Data.Maybe
-import Data.Monoid (Monoid(..))
 import Data.Ratio
-import Foreign.Ptr (Ptr, castPtr)
-import Foreign.Storable (Storable(..))
-import GHC.Generics
 import Numeric.Units.Dimensional.Dimensions
+import Numeric.Units.Dimensional.Internal
 import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen)
 import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name
-import Numeric.Units.Dimensional.UnitNames.InterchangeNames (HasInterchangeName(..))
 import Numeric.Units.Dimensional.Variants hiding (type (*))
 import qualified Numeric.Units.Dimensional.Variants as V
-import qualified Data.Vector.Generic.Mutable as M
-import qualified Data.Vector.Generic as G
-import qualified Data.Vector.Unboxed.Base as U
 
 {-
 We will reuse the operators and function names from the Prelude.
@@ -299,131 +282,38 @@ 
 -}
 
--- | A physical quantity or unit.
---
--- We call this data type 'Dimensional' to capture the notion that the
--- units and quantities it represents have physical dimensions.
--- 
--- The type variable 'a' is the only non-phantom type variable and
--- represents the numerical value of a quantity or the scale (w.r.t.
--- SI units) of a unit. For SI units the scale will always be 1. For
--- non-SI units the scale is the ratio of the unit to the SI unit with
--- the same physical dimension.
---
--- Since 'a' is the only non-phantom type we were able to define
--- 'Dimensional' as a newtype, avoiding boxing at runtime.
-class KnownVariant (v :: Variant) where
-  -- | A dimensional value, either a 'Quantity' or a 'Unit', parameterized by its 'Dimension' and representation.
-  data Dimensional v :: Dimension -> * -> *
-  extractValue :: Dimensional v d a -> (a, Maybe ExactPi)
-  extractName :: Dimensional v d a -> Maybe (UnitName 'NonMetric)
-  injectValue :: (Maybe (UnitName 'NonMetric)) -> (a, Maybe ExactPi) -> Dimensional v d a
-  -- | Maps over the underlying representation of a dimensional value.
-  -- The caller is responsible for ensuring that the supplied function respects the dimensional abstraction.
-  -- This means that the function must preserve numerical values, or linearly scale them while preserving the origin.
-  dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2
-
-deriving instance Typeable Dimensional
-
-instance KnownVariant 'DQuantity where
-  newtype Dimensional 'DQuantity d a = Quantity' a
-    deriving (Eq, Ord, Data, Generic, Generic1
-#if MIN_VERSION_base(4,8,0)
-     , Typeable -- GHC 7.8 doesn't support deriving this instance
-#endif
-    )
-  extractValue (Quantity' x) = (x, Nothing)
-  extractName _ = Nothing
-  injectValue _ (x, _) = Quantity' x
-  dmap f (Quantity' x) = Quantity' (f x)
-
-instance (Typeable m) => KnownVariant ('DUnit m) where
-  data Dimensional ('DUnit m) d a = Unit' !(UnitName m) !ExactPi !a
-    deriving (Generic, Generic1
-#if MIN_VERSION_base(4,8,0)
-     , Typeable -- GHC 7.8 doesn't support deriving this instance
-#endif
-    )
-  extractValue (Unit' _ e x) = (x, Just e)
-  extractName (Unit' n _ _) = Just . Name.weaken $ n
-  injectValue (Just n) (x, Just e) | Just n' <- relax n = Unit' n' e x
-                                   | otherwise          = Prelude.error "Shouldn't be reachable. Needed a metric name but got a non-metric one."
-  injectValue _        _ = Prelude.error "Shouldn't be reachable. Needed to name a quantity."
-  dmap f (Unit' n e x) = Unit' n e (f x)
-
--- | A unit of measurement.
-type Unit (m :: Metricality) = Dimensional ('DUnit m)
-
--- | A dimensional quantity.
-type Quantity = Dimensional 'DQuantity
-
--- GHC is somewhat unclear about why, but it won't derive this instance, so we give it explicitly.
-instance (Bounded a) => Bounded (Quantity d a) where
-  minBound = Quantity' minBound
-  maxBound = Quantity' maxBound
-
-instance HasInterchangeName (Unit m d a) where
-  interchangeName (Unit' n _ _) = interchangeName n
-
 -- | Extracts the 'UnitName' of a 'Unit'.
 name :: Unit m d a -> UnitName m
-name (Unit' n _ _) = n
+name (Unit n _ _) = n
 
 -- | Extracts the exact value of a 'Unit', expressed in terms of the SI coherent derived unit (see 'siUnit') of the same 'Dimension'.
 --
 -- Note that the actual value may in some cases be approximate, for example if the unit is defined by experiment.
 exactValue :: Unit m d a -> ExactPi
-exactValue (Unit' _ e _) = e
+exactValue (Unit _ e _) = e
 
 -- | Discards potentially unwanted type level information about a 'Unit'.
 weaken :: Unit m d a -> Unit 'NonMetric d a
-weaken (Unit' n e v) = Unit' (Name.weaken n) e v
+weaken (Unit n e v) = Unit (Name.weaken n) e v
 
 -- | Attempts to convert a 'Unit' which may or may not be 'Metric' to one
 -- which is certainly 'Metric'.
 strengthen :: Unit m d a -> Maybe (Unit 'Metric d a)
-strengthen (Unit' n e v) | Just n' <- Name.strengthen n = Just $ Unit' n' e v
-                         | otherwise                    = Nothing
+strengthen (Unit n e v) | Just n' <- Name.strengthen n = Just $ Unit n' e v
+                        | otherwise                    = Nothing
 
 -- | Forms the exact version of a 'Unit'.
 exactify :: Unit m d a -> Unit m d ExactPi
-exactify (Unit' n e _) = Unit' n e e
-
--- Operates on a dimensional value using a unary operation on values, possibly yielding a Unit.
-liftUntyped :: (KnownVariant v, KnownVariant (Weaken v)) => (ExactPi -> ExactPi) -> (a -> a) -> UnitNameTransformer -> (Dimensional v d1 a) -> (Dimensional (Weaken v) d2 a)
-liftUntyped fe f nt x = let (x', e') = extractValue x
-                            n = extractName x
-                            n' = (liftA nt) n
-                         in injectValue n' (f x', fmap fe e')
-
--- Operates on a dimensional value using a unary operation on values, yielding a Quantity.
-liftUntypedQ :: (KnownVariant v) => (a -> a) -> Dimensional v d1 a -> Quantity d2 a
-liftUntypedQ f x = let (x', _) = extractValue x
-                    in Quantity' (f x')
-
--- Combines two dimensional values using a binary operation on values, possibly yielding a Unit.
-liftUntyped2 :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2)) => (ExactPi -> ExactPi -> ExactPi) -> (a -> a -> a) -> UnitNameTransformer2 -> Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) d3 a
-liftUntyped2 fe f nt x1 x2 = let (x1', e1') = extractValue x1
-                                 (x2', e2') = extractValue x2
-                                 n1 = extractName x1
-                                 n2 = extractName x2
-                                 n' = (liftA2 nt) n1 n2
-                              in injectValue n' (f x1' x2', fe <$> e1' <*> e2') 
-
--- Combines two dimensional values using a binary operation on values, yielding a Quantity.
-liftUntyped2Q :: (KnownVariant v1, KnownVariant v2) => (a -> a -> a) -> Dimensional v1 d1 a -> Dimensional v2 d2 a -> Quantity d3 a
-liftUntyped2Q f x1 x2 = let (x1', _) = extractValue x1
-                            (x2', _) = extractValue x2
-                         in Quantity' (f x1' x2') 
+exactify (Unit n e _) = Unit n e e
 
 -- | Forms a 'Quantity' by multipliying a number and a unit.
 (*~) :: Num a => a -> Unit m d a -> Quantity d a
-x *~ (Unit' _ _ y) = Quantity' (x Prelude.* y)
+x *~ (Unit _ _ y) = Quantity (x Prelude.* y)
 
 -- | Divides a 'Quantity' by a 'Unit' of the same physical dimension, obtaining the
 -- numerical value of the quantity expressed in that unit.
 (/~) :: Fractional a => Quantity d a -> Unit m d a -> a
-(Quantity' x) /~ (Unit' _ _ y) = (x Prelude./ y)
+(Quantity x) /~ (Unit _ _ y) = (x Prelude./ y)
 
 {-
 We give '*~' and '/~' the same fixity as '*' and '/' defined below.
@@ -483,9 +373,9 @@ 
 We could provide the 'Mul' and 'Div' classes with full functional
 dependencies but that would be of limited utility as there is no
-obvious use for "backwards" type inference and would also limit
-what we can achieve overlapping instances. (In particular, it breaks
-the 'Extensible' module.)
+limited use for "backwards" type inference. Efforts are underway to
+develop a type-checker plugin that does enable these scenarios, e.g.
+for linear algebra.
 
 -}
 
@@ -504,14 +394,14 @@ -- The intimidating type signature captures the similarity between these operations
 -- and ensures that composite 'Unit's are 'NonMetric'.
 (*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) (d1 * d2) a
-(*) = liftUntyped2 (Prelude.*) (Prelude.*) (Name.*)
+(*) = liftD2 (Prelude.*) (Prelude.*) (Name.*)
 
 -- | Divides one 'Quantity' by another or one 'Unit' by another.
 --
 -- The intimidating type signature captures the similarity between these operations
 -- and ensures that composite 'Unit's are 'NotPrefixable'.
 (/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) (d1 / d2) a
-(/) = liftUntyped2 (Prelude./) (Prelude./) (Name./)
+(/) = liftD2 (Prelude./) (Prelude./) (Name./)
 
 -- | Raises a 'Quantity' or 'Unit' to an integer power.
 --
@@ -525,7 +415,7 @@ (^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v))
     => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a
 x ^ n = let n' = (toNum n) :: Int
-         in liftUntyped (Prelude.^^ n') (Prelude.^^ n') (Name.^ n') x
+         in liftD (Prelude.^^ n') (Prelude.^^ n') (Name.^ n') x
 
 {-
 A special case is that dimensionless quantities are not restricted
@@ -542,19 +432,19 @@ 
 -- | Negates the value of a 'Quantity'.
 negate :: Num a => Quantity d a -> Quantity d a
-negate = liftUntypedQ Prelude.negate
+negate = liftQ Prelude.negate
 
 -- | Adds two 'Quantity's.
 (+) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
-(+) = liftUntyped2Q (Prelude.+)
+(+) = liftQ2 (Prelude.+)
 
 -- | Subtracts one 'Quantity' from another.
 (-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
-x - y = x + negate y
+(-) = liftQ2 (Prelude.-)
 
 -- | Takes the absolute value of a 'Quantity'.
 abs :: Num a => Quantity d a -> Quantity d a
-abs = liftUntypedQ Prelude.abs
+abs = liftQ Prelude.abs
 
 {-
 Roots of arbitrary (integral) degree. Appears to occasionally be useful
@@ -574,7 +464,7 @@ nroot :: (KnownTypeInt n, Floating a)
       => Proxy n -> Quantity d a -> Quantity (Root d n) a
 nroot n = let n' = 1 Prelude./ toNum n
-           in liftUntypedQ (Prelude.** n')
+           in liftQ (Prelude.** n')
 
 {-
 We provide short-hands for the square and cubic roots.
@@ -615,16 +505,6 @@      => Quantity d a -> Proxy n -> Quantity (Root d n) a
 (^/) = flip nroot
 
-{-
-Since quantities form a monoid under addition, but not under multiplication unless they are dimensionless,
-we will define a monoid instance that adds.
--}
-
--- | 'Quantity's of a given 'Dimension' form a 'Monoid' under addition.
-instance (Num a) => Monoid (Quantity d a) where
-  mempty = _0
-  mappend = (+)
-
 {- $collections
 Here we define operators and functions to make working with homogenuous
 lists of dimensionals more convenient.
@@ -675,18 +555,6 @@     f i = xi + realToFrac (i % succ n') *~ one * (xf - xi)
 
 {-
-
-= Dimensionless =
-
-For dimensionless quantities pretty much any operation is applicable.
-We provide this freedom by making 'Dimensionless' an instance of
-'Functor'.
--}
-
-instance Functor Dimensionless where
-  fmap = dmap
-
-{-
 We continue by defining elementary functions on 'Dimensionless'
 that may be obviously useful.
 -}
@@ -710,18 +578,12 @@ 
 -- | Raises a dimensionless quantity to a floating power using 'Prelude.**'.
 (**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a
-(**) = liftUntyped2Q (Prelude.**)
+(**) = liftQ2 (Prelude.**)
 
 -- | The standard two argument arctangent function.
 -- Since it interprets its two arguments in comparison with one another, the input may have any dimension.
 atan2 :: (RealFloat a) => Quantity d a -> Quantity d a -> Dimensionless a
-atan2 = liftUntyped2Q Prelude.atan2
-
--- | A polymorphic 'Unit' which can be used in place of the coherent
--- SI base unit of any dimension. This allows polymorphic quantity
--- creation and destruction without exposing the 'Dimensional' constructor.
-siUnit :: forall d a.(KnownDimension d, Num a) => Unit 'NonMetric d a
-siUnit = Unit' (baseUnitName $ dimension (Proxy :: Proxy d)) 1 1
+atan2 = liftQ2 Prelude.atan2
 
 {-
 The only unit we will define in this module is 'one'.
@@ -734,7 +596,7 @@ -- appear in expressions. However, for us it is necessary to use 'one'
 -- as we would any other unit to perform the "boxing" of dimensionless values.
 one :: Num a => Unit 'NonMetric DOne a
-one = Unit' nOne 1 1
+one = Unit nOne 1 1
 
 {- $constants
 For convenience we define some constants for small integer values
@@ -747,7 +609,7 @@ -- it to express zero 'Length' or 'Capacitance' or 'Velocity' etc, in addition
 -- to the 'Dimensionless' value zero.
 _0 :: Num a => Quantity d a
-_0 = Quantity' 0
+_0 = Quantity 0
 
 _1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Num a) => Dimensionless a
 _1 = 1 *~ one
@@ -792,25 +654,6 @@ 
 -}
 
-instance (KnownDimension d) => HasDimension (Dimensional v d a) where
-  dimension _ = dimension (Proxy :: Proxy d)
-
-{-
-We will conclude by providing a reasonable 'Show' instance for
-quantities. The SI unit of the quantity is inferred
-from its dimension.
--}
-instance (KnownDimension d, Show a, Fractional a) => Show (Quantity d a) where
-  show = showIn siUnit
-
--- | Shows the value of a 'Quantity' expressed in a specified 'Unit' of the same 'Dimension'.
-showIn :: (KnownDimension d, Show a, Fractional a) => Unit m d a -> Quantity d a -> String
-showIn (Unit' n _ y) (Quantity' x) | Name.weaken n == nOne = show (x Prelude./ y)
-                                   | otherwise             = (show (x Prelude./ y)) ++ " " ++ (show n)
-
-instance (KnownDimension d, Show a) => Show (Unit m d a) where
-  show (Unit' n e x) = "The unit " ++ show n ++ ", with value " ++ show e ++ " (or " ++ show x ++ ")"
-
 -- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'.
 -- 
 -- Use this variant when the scale factor of the resulting unit is irrational or 'Approximate'. See 'mkUnitQ' for when it is rational
@@ -822,8 +665,8 @@ -- Supplying negative defining quantities is allowed and handled gracefully, but is discouraged
 -- on the grounds that it may be unexpected by other readers.
 mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a
-mkUnitR n s' (Unit' _ s x) | isExactZero s = error "Supplying zero as a conversion factor is not valid."
-                           | otherwise     = Unit' n (s' Prelude.* s) (approximateValue s' Prelude.* x)
+mkUnitR n s' (Unit _ s x) | isExactZero s = error "Supplying zero as a conversion factor is not valid."
+                          | otherwise     = Unit n (s' Prelude.* s) (approximateValue s' Prelude.* x)
 
 -- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'.
 --
@@ -832,9 +675,9 @@ --
 -- For more information see 'mkUnitR'.
 mkUnitQ :: Fractional a => UnitName m -> Rational -> Unit m1 d a -> Unit m d a
-mkUnitQ n s' (Unit' _ s _) | s' == 0                       = error "Supplying zero as a conversion factor is not valid."
-                           | Just q <- toExactRational s'' = Unit' n s'' (fromRational q)
-                           | otherwise                     = error "The resulting conversion factor is not an exact rational." 
+mkUnitQ n s' (Unit _ s _) | s' == 0                       = error "Supplying zero as a conversion factor is not valid."
+                          | Just q <- toExactRational s'' = Unit n s'' (fromRational q)
+                          | otherwise                     = error "The resulting conversion factor is not an exact rational." 
   where
     s'' = fromRational s' Prelude.* s                               
 
@@ -845,57 +688,8 @@ --
 -- For more information see 'mkUnitR'.
 mkUnitZ :: Num a => UnitName m -> Integer -> Unit m1 d a -> Unit m d a
-mkUnitZ n s' (Unit' _ s _) | s' == 0                      = error "Supplying zero as a conversion factor is not valid."
-                           | Just z <- toExactInteger s'' = Unit' n s'' (fromInteger z)
-                           | otherwise                    = error "The resulting conversion factor is not an exact integer."
+mkUnitZ n s' (Unit _ s _) | s' == 0                      = error "Supplying zero as a conversion factor is not valid."
+                          | Just z <- toExactInteger s'' = Unit n s'' (fromInteger z)
+                          | otherwise                    = error "The resulting conversion factor is not an exact integer."
   where
     s'' = fromInteger s' Prelude.* s
-
-instance NFData a => NFData (Quantity d a) -- instance is derived from Generic instance
-
-instance Storable a => Storable (Quantity d a) where
-  sizeOf _ = sizeOf (undefined::a)
-  {-# INLINE sizeOf #-}
-  alignment _ = alignment (undefined::a)
-  {-# INLINE alignment #-}
-  poke ptr = poke (castPtr ptr :: Ptr a) . coerce
-  {-# INLINE poke #-}
-  peek ptr = liftM Quantity' (peek (castPtr ptr :: Ptr a))
-  {-# INLINE peek #-}
-
-{-
-Instances for vectors of quantities.
--}
-newtype instance U.Vector (Quantity d a)    =  V_Quantity {unVQ :: U.Vector a}
-newtype instance U.MVector s (Quantity d a) = MV_Quantity {unMVQ :: U.MVector s a}
-instance U.Unbox a => U.Unbox (Quantity d a)
-
-instance (M.MVector U.MVector a) => M.MVector U.MVector (Quantity d a) where
-  basicLength          = M.basicLength . unMVQ
-  {-# INLINE basicLength #-}
-  basicUnsafeSlice m n = MV_Quantity . M.basicUnsafeSlice m n . unMVQ
-  {-# INLINE basicUnsafeSlice #-}
-  basicOverlaps u v    = M.basicOverlaps (unMVQ u) (unMVQ v)
-  {-# INLINE basicOverlaps #-}
-  basicUnsafeNew       = liftM MV_Quantity . M.basicUnsafeNew
-  {-# INLINE basicUnsafeNew #-}
-  basicUnsafeRead v    = liftM Quantity' . M.basicUnsafeRead (unMVQ v)
-  {-# INLINE basicUnsafeRead #-}
-  basicUnsafeWrite v i = M.basicUnsafeWrite (unMVQ v) i . coerce
-  {-# INLINE basicUnsafeWrite #-}
-#if MIN_VERSION_vector(0,11,0)
-  basicInitialize      = M.basicInitialize . unMVQ
-  {-# INLINE basicInitialize #-}
-#endif
-
-instance (G.Vector U.Vector a) => G.Vector U.Vector (Quantity d a) where
-  basicUnsafeFreeze    = liftM V_Quantity  . G.basicUnsafeFreeze . unMVQ
-  {-# INLINE basicUnsafeFreeze #-}
-  basicUnsafeThaw      = liftM MV_Quantity . G.basicUnsafeThaw   . unVQ
-  {-# INLINE basicUnsafeThaw #-}
-  basicLength          = G.basicLength . unVQ
-  {-# INLINE basicLength #-}
-  basicUnsafeSlice m n = V_Quantity . G.basicUnsafeSlice m n . unVQ
-  {-# INLINE basicUnsafeSlice #-}
-  basicUnsafeIndexM v  = liftM Quantity' . G.basicUnsafeIndexM (unVQ v)
-  {-# INLINE basicUnsafeIndexM #-}
+ src/Numeric/Units/Dimensional/Coercion.hs view
@@ -0,0 +1,27 @@+{- |
+    Copyright  : Copyright (C) 2006-2014 Bjorn Buckwalter
+    License    : BSD3
+
+    Maintainer : bjorn@buckwalter.se
+    Stability  : Experimental
+    Portability: GHC only?
+
+Re-exports the raw 'Quantity' constructor from the Numeric.Units.Dimensional.Internal module, along with 'Data.Coerce.coerce',
+for convenience in converting between raw representations and dimensional values.
+
+Note that use of these constructs requires the user to verify the dimensional safety of the conversion,
+because the coercion doesn't explicitly mention the unit of the representation.
+
+Note that the haddock documentation doesn't mention the 'Quantity' constructor because it is a part of the
+'Dimensional' associated data family, but it is exported by this module.
+
+-}
+
+module Numeric.Units.Dimensional.Coercion
+(
+  coerce, Dimensional(Quantity)
+)
+where
+
+import Data.Coerce (coerce)
+import Numeric.Units.Dimensional.Internal (Dimensional(Quantity))
src/Numeric/Units/Dimensional/Dimensions.hs view
@@ -22,4 +22,4 @@ where
 
 import Numeric.Units.Dimensional.Dimensions.TermLevel hiding ((*), (/), (^), recip, dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity)
-import Numeric.Units.Dimensional.Dimensions.TypeLevel+import Numeric.Units.Dimensional.Dimensions.TypeLevel
src/Numeric/Units/Dimensional/Dynamic.hs view
@@ -16,13 +16,16 @@ 
 module Numeric.Units.Dimensional.Dynamic
 (
+  -- * Dynamic Quantities
   AnyQuantity
 , demoteQuantity, promoteQuantity
+  -- * Dynamic Units
 , AnyUnit
 , demoteUnit, promoteUnit
 ) where
 
 import Numeric.Units.Dimensional.Prelude hiding (lookup)
+import Numeric.Units.Dimensional.Coercion
 import Numeric.Units.Dimensional.UnitNames (UnitName, baseUnitName)
 import Data.ExactPi
 import Data.Proxy
@@ -38,16 +41,16 @@   dimension (AnyQuantity d _) = d
 
 -- | Converts a 'Quantity' of statically known 'Dimension' into an 'AnyQuantity'.
-demoteQuantity :: forall d v.(KnownDimension d, Fractional v) => Quantity d v -> AnyQuantity v
-demoteQuantity val = AnyQuantity dim (val /~ siUnit)
+demoteQuantity :: forall d v.(KnownDimension d) => Quantity d v -> AnyQuantity v
+demoteQuantity (Quantity val) = AnyQuantity dim val
   where dim = dimension (Proxy :: Proxy d)
 
 -- | Converts an 'AnyQuantity' into a 'Quantity' of statically known 'Dimension', or 'Nothing' if the dimension does not match.
-promoteQuantity :: forall d v.(KnownDimension d, Fractional v) => AnyQuantity v -> Maybe (Quantity d v)
-promoteQuantity (AnyQuantity dim val) | dim == dim' = Just $ val *~ siUnit
+promoteQuantity :: forall d v.(KnownDimension d) => AnyQuantity v -> Maybe (Quantity d v)
+promoteQuantity (AnyQuantity dim val) | dim == dim' = Just . Quantity $ val
                                       | otherwise   = Nothing
-                                                    where
-                                                      dim' = dimension (Proxy :: Proxy d)
+  where
+    dim' = dimension (Proxy :: Proxy d)
 
 -- | A 'Unit' whose 'Dimension' is only known dynamically.
 data AnyUnit = AnyUnit Dimension' (UnitName 'NonMetric) ExactPi
+ src/Numeric/Units/Dimensional/Internal.hs view
@@ -0,0 +1,237 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE MultiParamTypeClasses #-} -- for Vector instances only
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module Numeric.Units.Dimensional.Internal
+(
+  KnownVariant(..),
+  Dimensional(..),
+  type Unit, type Quantity,
+  siUnit, showIn,
+  liftD, liftD2,
+  liftQ, liftQ2
+)
+where
+
+import Control.Applicative
+import Control.DeepSeq
+import Control.Monad (liftM)
+import Data.Coerce (coerce)
+import Data.Data
+import Data.ExactPi
+import Data.Monoid (Monoid(..))
+import Foreign.Ptr (Ptr, castPtr)
+import Foreign.Storable (Storable(..))
+import GHC.Generics
+import Numeric.Units.Dimensional.Dimensions
+import Numeric.Units.Dimensional.Variants hiding (type (*))
+import qualified Numeric.Units.Dimensional.Variants as V
+import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen)
+import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name
+import Numeric.Units.Dimensional.UnitNames.InterchangeNames (HasInterchangeName(..))
+import qualified Data.Vector.Generic.Mutable as M
+import qualified Data.Vector.Generic as G
+import qualified Data.Vector.Unboxed.Base as U
+import Prelude
+  ( Show, Eq(..), Ord, Bounded(..), Num, Fractional, Functor
+  , String, Maybe(..)
+  , (.), ($), (++), (+), (/)
+  , show, otherwise, undefined, error, fmap
+  )
+
+-- | A unit of measurement.
+type Unit (m :: Metricality) = Dimensional ('DUnit m)
+
+-- | A dimensional quantity.
+type Quantity = Dimensional 'DQuantity
+
+-- | A physical quantity or unit.
+--
+-- We call this data type 'Dimensional' to capture the notion that the
+-- units and quantities it represents have physical dimensions.
+-- 
+-- The type variable 'a' is the only non-phantom type variable and
+-- represents the numerical value of a quantity or the scale (w.r.t.
+-- SI units) of a unit. For SI units the scale will always be 1. For
+-- non-SI units the scale is the ratio of the unit to the SI unit with
+-- the same physical dimension.
+--
+-- Since 'a' is the only non-phantom type we were able to define
+-- 'Dimensional' as a newtype, avoiding boxing at runtime.
+class KnownVariant (v :: Variant) where
+  -- | A dimensional value, either a 'Quantity' or a 'Unit', parameterized by its 'Dimension' and representation.
+  data Dimensional v :: Dimension -> * -> *
+  extractValue :: Dimensional v d a -> (a, Maybe ExactPi)
+  extractName :: Dimensional v d a -> Maybe (UnitName 'NonMetric)
+  injectValue :: (Maybe (UnitName 'NonMetric)) -> (a, Maybe ExactPi) -> Dimensional v d a
+  -- | Maps over the underlying representation of a dimensional value.
+  -- The caller is responsible for ensuring that the supplied function respects the dimensional abstraction.
+  -- This means that the function must preserve numerical values, or linearly scale them while preserving the origin.
+  dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2
+
+deriving instance Typeable Dimensional
+
+instance KnownVariant 'DQuantity where
+  newtype Dimensional 'DQuantity d a = Quantity a
+    deriving (Eq, Ord, Data, Generic, Generic1
+#if MIN_VERSION_base(4,8,0)
+     , Typeable -- GHC 7.8 doesn't support deriving this instance
+#endif
+    )
+  extractValue (Quantity x) = (x, Nothing)
+  extractName _ = Nothing
+  injectValue _ (x, _) = Quantity x
+  dmap = coerce
+
+instance (Typeable m) => KnownVariant ('DUnit m) where
+  data Dimensional ('DUnit m) d a = Unit !(UnitName m) !ExactPi !a
+    deriving (Generic, Generic1
+#if MIN_VERSION_base(4,8,0)
+     , Typeable -- GHC 7.8 doesn't support deriving this instance
+#endif
+    )
+  extractValue (Unit _ e x) = (x, Just e)
+  extractName (Unit n _ _) = Just . Name.weaken $ n
+  injectValue (Just n) (x, Just e) | Just n' <- relax n = Unit n' e x
+                                   | otherwise          = error "Shouldn't be reachable. Needed a metric name but got a non-metric one."
+  injectValue _        _ = error "Shouldn't be reachable. Needed to name a quantity."
+  dmap f (Unit n e x) = Unit n e (f x)
+
+-- GHC is somewhat unclear about why, but it won't derive this instance, so we give it explicitly.
+instance (Bounded a) => Bounded (Quantity d a) where
+  minBound = Quantity minBound
+  maxBound = Quantity maxBound
+
+instance HasInterchangeName (Unit m d a) where
+  interchangeName (Unit n _ _) = interchangeName n
+
+{-
+Since quantities form a monoid under addition, but not under multiplication unless they are dimensionless,
+we will define a monoid instance that adds.
+-}
+
+-- | 'Quantity's of a given 'Dimension' form a 'Monoid' under addition.
+instance (Num a) => Monoid (Quantity d a) where
+  mempty = Quantity 0
+  mappend = liftQ2 (+)
+
+{-
+
+= Dimensionless =
+
+For dimensionless quantities pretty much any operation is applicable.
+We provide this freedom by making 'Dimensionless' an instance of
+'Functor'.
+-}
+
+instance Functor (Quantity DOne) where
+  fmap = dmap
+
+instance (KnownDimension d) => HasDimension (Dimensional v d a) where
+  dimension _ = dimension (Proxy :: Proxy d)
+
+-- | A polymorphic 'Unit' which can be used in place of the coherent
+-- SI base unit of any dimension. This allows polymorphic quantity
+-- creation and destruction without exposing the 'Dimensional' constructor.
+siUnit :: forall d a.(KnownDimension d, Num a) => Unit 'NonMetric d a
+siUnit = Unit (baseUnitName $ dimension (Proxy :: Proxy d)) 1 1
+
+instance NFData a => NFData (Quantity d a) -- instance is derived from Generic instance
+
+instance Storable a => Storable (Quantity d a) where
+  sizeOf _ = sizeOf (undefined::a)
+  {-# INLINE sizeOf #-}
+  alignment _ = alignment (undefined::a)
+  {-# INLINE alignment #-}
+  poke ptr = poke (castPtr ptr :: Ptr a) . coerce
+  {-# INLINE poke #-}
+  peek ptr = liftM Quantity (peek (castPtr ptr :: Ptr a))
+  {-# INLINE peek #-}
+
+{-
+Instances for vectors of quantities.
+-}
+newtype instance U.Vector (Quantity d a)    =  V_Quantity {unVQ :: U.Vector a}
+newtype instance U.MVector s (Quantity d a) = MV_Quantity {unMVQ :: U.MVector s a}
+instance U.Unbox a => U.Unbox (Quantity d a)
+
+instance (M.MVector U.MVector a) => M.MVector U.MVector (Quantity d a) where
+  basicLength          = M.basicLength . unMVQ
+  {-# INLINE basicLength #-}
+  basicUnsafeSlice m n = MV_Quantity . M.basicUnsafeSlice m n . unMVQ
+  {-# INLINE basicUnsafeSlice #-}
+  basicOverlaps u v    = M.basicOverlaps (unMVQ u) (unMVQ v)
+  {-# INLINE basicOverlaps #-}
+  basicUnsafeNew       = liftM MV_Quantity . M.basicUnsafeNew
+  {-# INLINE basicUnsafeNew #-}
+  basicUnsafeRead v    = liftM Quantity . M.basicUnsafeRead (unMVQ v)
+  {-# INLINE basicUnsafeRead #-}
+  basicUnsafeWrite v i = M.basicUnsafeWrite (unMVQ v) i . coerce
+  {-# INLINE basicUnsafeWrite #-}
+#if MIN_VERSION_vector(0,11,0)
+  basicInitialize      = M.basicInitialize . unMVQ
+  {-# INLINE basicInitialize #-}
+#endif
+
+instance (G.Vector U.Vector a) => G.Vector U.Vector (Quantity d a) where
+  basicUnsafeFreeze    = liftM V_Quantity  . G.basicUnsafeFreeze . unMVQ
+  {-# INLINE basicUnsafeFreeze #-}
+  basicUnsafeThaw      = liftM MV_Quantity . G.basicUnsafeThaw   . unVQ
+  {-# INLINE basicUnsafeThaw #-}
+  basicLength          = G.basicLength . unVQ
+  {-# INLINE basicLength #-}
+  basicUnsafeSlice m n = V_Quantity . G.basicUnsafeSlice m n . unVQ
+  {-# INLINE basicUnsafeSlice #-}
+  basicUnsafeIndexM v  = liftM Quantity . G.basicUnsafeIndexM (unVQ v)
+  {-# INLINE basicUnsafeIndexM #-}
+
+{-
+We will conclude by providing a reasonable 'Show' instance for
+quantities. The SI unit of the quantity is inferred
+from its dimension.
+-}
+instance (KnownDimension d, Show a, Fractional a) => Show (Quantity d a) where
+  show = showIn siUnit
+
+-- | Shows the value of a 'Quantity' expressed in a specified 'Unit' of the same 'Dimension'.
+showIn :: (KnownDimension d, Show a, Fractional a) => Unit m d a -> Quantity d a -> String
+showIn (Unit n _ y) (Quantity x) | Name.weaken n == nOne = show (x / y)
+                                 | otherwise             = (show (x / y)) ++ " " ++ (show n)
+
+instance (KnownDimension d, Show a) => Show (Unit m d a) where
+  show (Unit n e x) = "The unit " ++ show n ++ ", with value " ++ show e ++ " (or " ++ show x ++ ")"
+
+-- Operates on a dimensional value using a unary operation on values, possibly yielding a Unit.
+liftD :: (KnownVariant v1, KnownVariant v2) => (ExactPi -> ExactPi) -> (a -> b) -> UnitNameTransformer -> (Dimensional v1 d1 a) -> (Dimensional v2 d2 b)
+liftD fe f nt x = let (x', e') = extractValue x
+                      n = extractName x
+                      n' = (liftA nt) n
+                   in injectValue n' (f x', fmap fe e')
+
+-- Operates on a dimensional value using a unary operation on values, yielding a Quantity.
+liftQ :: (a -> a) -> Quantity d1 a -> Quantity d2 a
+liftQ = coerce
+
+-- Combines two dimensional values using a binary operation on values, possibly yielding a Unit.
+liftD2 :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2)) => (ExactPi -> ExactPi -> ExactPi) -> (a -> a -> a) -> UnitNameTransformer2 -> Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) d3 a
+liftD2 fe f nt x1 x2 = let (x1', e1') = extractValue x1
+                           (x2', e2') = extractValue x2
+                           n1 = extractName x1
+                           n2 = extractName x2
+                           n' = (liftA2 nt) n1 n2
+                        in injectValue n' (f x1' x2', fe <$> e1' <*> e2')
+
+-- Combines two dimensional values using a binary operation on values, yielding a Quantity.
+liftQ2 :: (a -> a -> a) -> Quantity d1 a -> Quantity d2 a -> Quantity d3 a
+liftQ2 = coerce
src/Numeric/Units/Dimensional/NonSI.hs view
@@ -165,8 +165,7 @@   several kinds of day), it is best to regard a year as a julian
   year of 365.25 days (31.5576 Ms) unless otherwise specified.
 
-This aligns well with my needs so I'm happy to oblige. We define
-the year in terms of seconds in order to avoid a 'Fractional'
+We define the year in terms of seconds in order to avoid a 'Fractional'
 constraint, and also provide a Julian century.
 
 -}