diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,123 @@
+Release notes for `units`
+=========================
+
+Version 2.4.1.5
+---------------
+* Use the `(-)` operator of the underlying `Num` instance in `(|-|)`.
+  This prevents crashes when the underlying number type does not support
+  negation. (Issue #69)
+* Compatibility with GHC 9.2.
+
+Version 2.4.1.4
+---------------
+* Compatibility with GHC 9.0.
+
+Version 2.4.1.3
+---------------
+* Compatibility with GHC 8.10, thanks to @ocharles.
+
+Version 2.4.1.2
+---------------
+* Compatibility with singletons 2.6 and GHC 8.8, further thanks to @ocharles.
+
+Version 2.4.1.1
+---------------
+* Fix some GHC compatibility issues, thanks to @ocharles.
+
+Version 2.4.1
+-------------
+* Add `Units` superclass to `UnitFactor`, easing type inference in
+GHC 8.0, thanks to @rimmington.
+
+Version 2.4
+-----------
+* New interface with the `linear` package in `Data.Metrology.Linear`.
+
+* New `Show` and `Read` instances for dimensionless quantities.
+
+* New `NFData` instances for quantities, thanks to @rimmington.
+
+* GHC 8 compatibility.
+
+Version 2.3
+-----------
+* `Data.Metrology.TH.evalType` now works in GHC 7.10 on dimensions like `Volume` instead
+of just base dimensions.
+
+* Break out the units parser into its own package: `units-parser`.
+
+* Add `Data.Metrology.Unsafe.UnsafeQu`, which has `Functor` and other
+instances.
+
+* Fix the fixity of `%`. It was way too high! Now it's 5.
+
+* Add the ability to convert in and out of point quantities. See `quOfPoint`
+and friends in `Data.Metrology.Vector`.
+
+Version 2.2.1
+-------------
+
+* Compatibility with GHC 7.10.
+
+* Added `AdditiveGroup` and `VectorSpace` instances for `Qu`.
+
+Version 2.2
+-----------
+
+* Some types of arithmetic operations are different to aid in type inference.
+For example, `*|` does not normalize its dimension list.
+
+* The types of the `derive...` TH functions now allow for deriving units
+based on composite dimensions/units.
+
+* New TH function to help declare constants, called `declareConstant`.
+
+Version 2.1
+-----------
+
+* Includes a decently comprehensive test suite.
+
+* Add support for unit parsing within expressions:
+
+      g = 9.8 % [si| m/s^2 |]
+
+  See `Data.Metrology.Parser`.
+
+* Now, `Data.Metrology` exports operators that work with the default
+  LCSU. Use `Data.Metrology.Poly` to get the old, more flexible operators.
+
+* Moved `showIn` from `Data.Metrology.Show` to `Data.Metrology.Poly`. This
+  allows users to import `showIn` without a `Show` instance for quantities.
+
+* Numeric operations are available based on vector spaces, as implemented in
+  the `vector-space` library. See `Data.Metrology.Vector`.
+
+* Some documentation cleanup.
+
+* New function `evalType` that evaluates a type, using Template Haskell. This
+  allows for easier instance declarations for quantities.
+
+* New class `Quantity` that allows for easy conversions with non-`units` types.
+
+* A few bugfixes.
+
+* The `Eq` and `Ord` instances now work over any quantity, not just dimensionless ones.
+
+* New functions in `Data.Metrology.TH` that define `Dimension` and `Unit` instances
+  for you.
+
+Version 2.0
+-----------
+
+This is a major update. `units` now supports the notion of a local unit set
+and of separable dimensions and units. See the description in the
+[draft paper](http://www.cis.upenn.edu/~eir/papers/2014/units/units.pdf) for
+more info.
+
+This update will very likely break any code that used `units-1.x`.
+
+The update was written in partnership with Takayuki Muranushi.
+
 Version 1.1
 -----------
 
diff --git a/Data/Metrology.hs b/Data/Metrology.hs
--- a/Data/Metrology.hs
+++ b/Data/Metrology.hs
@@ -1,8 +1,8 @@
 {- Data/Metrology.hs
 
    The units Package
-   Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   Copyright (c) 2014 Richard Eisenberg
+   rae@cs.brynmawr.edu
 
    This file gathers and exports all user-visible pieces of the units package.
    It also defines the main creators and consumers of dimensioned objects.
@@ -21,108 +21,50 @@
      :     units & dimensions, at both type and term levels
 -}
 
-{-# LANGUAGE ExplicitNamespaces, DataKinds, FlexibleInstances, TypeFamilies,
-             TypeOperators, ConstraintKinds, ScopedTypeVariables,
-             FlexibleContexts #-}
-
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Metrology
--- Copyright   :  (C) 2013 Richard Eisenberg
+-- Copyright   :  (C) 2014 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- The units package is a framework for strongly-typed dimensional analysis.
 -- This haddock documentation is generally /not/ enough to be able to use this
 -- package effectively. Please see the readme at
--- <http://www.cis.upenn.edu/~eir/packages/units/README.html>.
+-- <https://github.com/goldfirere/units/blob/master/README.md>.
 --
 -- Some of the types below refer to declarations that are not exported and
 -- not documented here. This is because Haddock does not allow finely-tuned
--- abstraction in documentation. (In particular, right-hand sides of type 
+-- abstraction in documentation. (In particular, right-hand sides of type
 -- synonym declarations are always included.) If a symbol is not exported,
 -- you do /not/ need to know anything about it to use this package.
 --
--- Though it doesn't appear here, @Scalar@ is an instance of @Num@, and
+-- Though it doesn't appear here, @Count@ is an instance of @Num@, and
 -- generally has all the numeric instances that @Double@ has.
+--
+-- This module exports definitions that lack unit-polymorphism. If you wish
+-- to write more polymorphic code, see 'Data.Metrology.Poly'. If you wish
+-- to use the numerical hierarchy from the @vector-space@ package, see
+-- 'Data.Metrology.Vector'.
 -----------------------------------------------------------------------------
 
-module Data.Metrology (
-  -- * Term-level combinators
-
-  -- | The term-level arithmetic operators are defined by
-  -- applying vertical bar(s) to the sides the dimensioned 
-  -- quantities acts on. 
-  -- See also "Data.Metrology.AltOperators" for an alternative system of operators.
-  (|+|), (|-|), 
-  (|*|), (|/|), (*|),  (|*), (/|), (|/), 
-  (|^), (|^^),
-  (|<|), (|>|), (|<=|), (|>=|), (|==|), (|/=|),
-  qApprox, qNapprox,        
-  qSq, qCube, qSqrt, qCubeRoot, nthRoot, 
-
-  -- * Nondimensional units, conversion between quantities and numeric values
-  unity, zero, redim, convert,
-  numIn, (#), (##), quOf, (%), (%%), defaultLCSU, fromDefaultLCSU,
-  constant,
-
-  -- * Type-level unit combinators
-  (:*)(..), (:/)(..), (:^)(..), (:@)(..),
-  UnitPrefix(..),
-
-  -- * Type-level quantity combinators
-  type (%*), type (%/), type (%^),
-
-  -- * Creating quantity types
-  Qu, MkQu_D, MkQu_DLN, MkQu_U, MkQu_ULN, 
-
-  -- * Creating new dimensions
-  Dimension,
-
-  -- * Creating new units
-  Unit(type BaseUnit, type DimOfUnit, conversionRatio), 
-  Canonical,
-
-  -- * Scalars, the only built-in unit
-  Dimensionless(..), Number(..), Scalar, scalar,
-
-  -- * LCSUs (locally coherent system of units)
-  MkLCSU, LCSU(DefaultLCSU), DefaultUnitOfDim,
-
-  -- * Validity checks and assertions
-  CompatibleUnit, CompatibleDim, ConvertibleLCSUs_D,
-  DefaultConvertibleLCSU_D, DefaultConvertibleLCSU_U,
-
-  -- * Type-level integers
-  Z(..), Succ, Pred, type (#+), type (#-), type (#*), type (#/), NegZ,
-
-  -- ** Synonyms for small numbers
-  One, Two, Three, Four, Five, MOne, MTwo, MThree, MFour, MFive,
+{-# LANGUAGE TypeOperators, ConstraintKinds, DataKinds #-}
 
-  -- ** Term-level singletons
-  pZero, pOne, pTwo, pThree, pFour, pFive,
-  pMOne, pMTwo, pMThree, pMFour, pMFive,
-  pSucc, pPred,
+module Data.Metrology (
+  -- * Operators working with a default LCSU
+  numIn, (#), quOf, (%), Count,
 
-  -- * Internal definitions
-  -- | The following module is re-exported solely to prevent noise in error messages;
-  -- we do not recommend trying to use these definitions in user code.
-  module Data.Metrology.Internal
+  -- * The rest of the @units@ package interface.
 
+  -- | Though Haddock doesn't show it, the polymorphic versions of 'numIn',
+  -- '#', 'quOf', '%', and 'Count' are not re-exported.
+  module Data.Metrology.Poly
   ) where
 
-import Data.Metrology.Z
-import Data.Metrology.Quantity
-import Data.Metrology.Dimensions
-import Data.Metrology.Factor
-import Data.Metrology.Units
-import Data.Metrology.Combinators
-import Data.Metrology.LCSU
-import Data.Metrology.Validity
-import Data.Metrology.Internal
-import Data.Proxy
+import Data.Metrology.Poly hiding ( numIn, (#), quOf, (%), Count )
+import qualified Data.Metrology.Poly as Poly
 
 -- | Extracts a numerical value from a dimensioned quantity, expressed in
 --   the given unit. For example:
@@ -132,31 +74,19 @@
 --
 --   or
 --
---   > inMeters x = x # Meter   
-numIn :: forall unit dim lcsu n.
-         ( ValidDLU dim lcsu unit
+--   > inMeters x = x # Meter
+numIn :: ( ValidDLU dim DefaultLCSU unit
          , Fractional n )
-      => Qu dim lcsu n -> unit -> n
-numIn (Qu val) u
-  = val * fromRational
-            (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))
-             / canonicalConvRatio u)
+      => Qu dim DefaultLCSU n -> unit -> n
+numIn = Poly.numIn
 
 infix 5 #
 -- | Infix synonym for 'numIn'
-(#) :: ( ValidDLU dim lcsu unit
+(#) :: ( ValidDLU dim DefaultLCSU unit
        , Fractional n )
-    => Qu dim lcsu n -> unit -> n
+    => Qu dim DefaultLCSU n -> unit -> n
 (#) = numIn
 
-infix 5 ##
--- | Like '#', but uses a default LCSU. This operator is recommended
--- for users who wish not to worry about LCSUs.
-(##) :: ( ValidDLU dim DefaultLCSU unit
-        , Fractional n )
-     => Qu dim DefaultLCSU n -> unit -> n
-(##) = numIn
-
 -- | Creates a dimensioned quantity in the given unit. For example:
 --
 --   > height :: Length
@@ -165,102 +95,19 @@
 --   or
 --
 --   > height = 2.0 % Meter
-quOf :: forall unit dim lcsu n.
-         ( ValidDLU dim lcsu unit
-         , Fractional n )
-      => n -> unit -> Qu dim lcsu n
-quOf d u
-  = Qu (d * fromRational
-               (canonicalConvRatio u
-                / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))))
+quOf :: ( ValidDLU dim DefaultLCSU unit
+        , Fractional n )
+      => n -> unit -> Qu dim DefaultLCSU n
+quOf = Poly.quOf
 
-infixr 9 %
+infixr 5 %
 -- | Infix synonym for 'quOf'
-(%) :: ( ValidDLU dim lcsu unit
+(%) :: ( ValidDLU dim DefaultLCSU unit
        , Fractional n )
-    => n -> unit -> Qu dim lcsu n
+    => n -> unit -> Qu dim DefaultLCSU n
 (%) = quOf
 
-infixr 9 %%
--- | Like '%', but uses a default LCSU. This operator is recommended
--- for users who wish not to worry about LCSUs.
-(%%) :: ( ValidDLU dim DefaultLCSU unit
-        , Fractional n )
-     => n -> unit -> Qu dim DefaultLCSU n
-(%%) = quOf
-
--- | Use this to choose a default LCSU for a dimensioned quantity.
--- The default LCSU uses the 'DefaultUnitOfDim' representation for each
--- dimension.
-defaultLCSU :: Qu dim DefaultLCSU n -> Qu dim DefaultLCSU n
-defaultLCSU = id
-
--- | The number 1, expressed as a unitless dimensioned quantity.
-unity :: Num n => Qu '[] l n
-unity = Qu 1
-
--- | The number 0, polymorphic in its dimension. Use of this will
--- often require a type annotation.
-zero :: Num n => Qu dimspec l n
-zero = Qu 0
-
--- | Cast between equivalent dimension within the same CSU.
---  for example [kg m s] and [s m kg]. See the README for more info.
-redim :: (d @~ e) => Qu d l n -> Qu e l n
-redim (Qu x) = Qu x
-
--- | Dimension-keeping cast between different CSUs.
-convert :: forall d l1 l2 n. 
-  ( ConvertibleLCSUs d l1 l2
-  , Fractional n ) 
-  => Qu d l1 n -> Qu d l2 n
-convert (Qu x) = Qu $ x * fromRational (
-  canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l1))
-  / canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l2)))
-
-
--- | Compute the argument in the DefaultLCSU, and present the result
--- as lcsu-polymorphic dimension-polymorphic value.
-fromDefaultLCSU :: ( d @~ e
-                   , ConvertibleLCSUs e DefaultLCSU l
-                   , Fractional n )
-         => Qu d DefaultLCSU n -> Qu e l n
-fromDefaultLCSU = convert . redim
-
-
--- | A synonym of 'fromDefaultLCSU', for one of its dominant usecase
--- is to inject constant quantities into dimension-polymorphic
--- expressions.
-constant :: ( d @~ e
-            , ConvertibleLCSUs e DefaultLCSU l
-            , Fractional n )
-         => Qu d DefaultLCSU n -> Qu e l n
-constant = fromDefaultLCSU
-
--------------------------------------------------------------
---- "Number" unit -------------------------------------------
--------------------------------------------------------------
-
--- | The dimension for the dimensionless quantities.
--- It is also called "quantities of dimension one", but
--- @One@ is confusing with the type-level integer One.
-data Dimensionless = Dimensionless
-instance Dimension Dimensionless where
-  type DimFactorsOf Dimensionless = '[]
-type instance DefaultUnitOfDim Dimensionless = Number
-
--- | The unit for unitless dimensioned quantities
-data Number = Number -- the unit for unadorned numbers
-instance Unit Number where
-  type BaseUnit Number = Canonical
-  type DimOfUnit Number = Dimensionless
-  type UnitFactorsOf Number = '[]
-
 -- | The type of unitless dimensioned quantities.
 -- This is an instance of @Num@, though Haddock doesn't show it.
--- This uses a @Double@ internally and uses a default LCSU.
-type Scalar = MkQu_U Number
-
--- | Convert a raw number into a unitless dimensioned quantity
-scalar :: n -> Qu '[] l n
-scalar = Qu
+-- This assumes a default LCSU and an internal representation of @Double@.
+type Count = MkQu_U Number
diff --git a/Data/Metrology/Combinators.hs b/Data/Metrology/Combinators.hs
--- a/Data/Metrology/Combinators.hs
+++ b/Data/Metrology/Combinators.hs
@@ -2,18 +2,22 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file defines combinators to build more complex units and dimensions from simpler ones.
 -}
 
-{-# LANGUAGE TypeOperators, TypeFamilies, UndecidableInstances, 
+{-# LANGUAGE TypeOperators, TypeFamilies, UndecidableInstances,
              ScopedTypeVariables, DataKinds, FlexibleInstances,
-             ConstraintKinds #-}
+             ConstraintKinds, CPP #-}
 
+#if __GLASGOW_HASKELL__ >= 711
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+#endif
+
 module Data.Metrology.Combinators where
 
-import Data.Singletons ( Sing, SingI, sing )
+import Data.Singletons ( SingI, sing )
 
 import Data.Metrology.Dimensions
 import Data.Metrology.Units
@@ -28,7 +32,8 @@
 data u1 :* u2 = u1 :* u2
 
 instance (Dimension d1, Dimension d2) => Dimension (d1 :* d2) where
-  type DimFactorsOf (d1 :* d2) = (DimFactorsOf d1) @+ (DimFactorsOf d2)
+  type DimFactorsOf (d1 :* d2)
+    = Normalize ((DimFactorsOf d1) @+ (DimFactorsOf d2))
 
 instance (Unit u1, Unit u2) => Unit (u1 :* u2) where
 
@@ -37,48 +42,65 @@
   type DimOfUnit (u1 :* u2) = DimOfUnit u1 :* DimOfUnit u2
   conversionRatio _ = undefined -- this should never be called
 
-  type UnitFactorsOf (u1 :* u2) = (UnitFactorsOf u1) @+ (UnitFactorsOf u2)
+  type UnitFactorsOf (u1 :* u2)
+    = Normalize ((UnitFactorsOf u1) @+ (UnitFactorsOf u2))
   canonicalConvRatio _ = canonicalConvRatio (undefined :: u1) *
                          canonicalConvRatio (undefined :: u2)
 
 type instance DefaultUnitOfDim (d1 :* d2) =
   DefaultUnitOfDim d1 :* DefaultUnitOfDim d2
 
+instance (Show u1, Show u2) => Show (u1 :* u2) where
+  show _ = show (undefined :: u1) ++ " " ++ show (undefined :: u2)
+
+
 infixl 7 :/
 -- | Divide two units to get another unit
 data u1 :/ u2 = u1 :/ u2
 
 instance (Dimension d1, Dimension d2) => Dimension (d1 :/ d2) where
-  type DimFactorsOf (d1 :/ d2) = (DimFactorsOf d1) @- (DimFactorsOf d2)
+  type DimFactorsOf (d1 :/ d2)
+    = Normalize ((DimFactorsOf d1) @- (DimFactorsOf d2))
 
 instance (Unit u1, Unit u2) => Unit (u1 :/ u2) where
   type BaseUnit (u1 :/ u2) = Canonical
   type DimOfUnit (u1 :/ u2) = DimOfUnit u1 :/ DimOfUnit u2
   conversionRatio _ = undefined -- this should never be called
-  type UnitFactorsOf (u1 :/ u2) = (UnitFactorsOf u1) @- (UnitFactorsOf u2)
+  type UnitFactorsOf (u1 :/ u2)
+    = Normalize ((UnitFactorsOf u1) @- (UnitFactorsOf u2))
   canonicalConvRatio _ = canonicalConvRatio (undefined :: u1) /
                          canonicalConvRatio (undefined :: u2)
 
 type instance DefaultUnitOfDim (d1 :/ d2) =
   DefaultUnitOfDim d1 :/ DefaultUnitOfDim d2
-  
+
+instance (Show u1, Show u2) => Show (u1 :/ u2) where
+  show _ = show (undefined :: u1) ++ "/" ++ show (undefined :: u2)
+
+
 infixr 8 :^
 -- | Raise a unit to a power, known at compile time
 data unit :^ (power :: Z) = unit :^ Sing power
 
 instance Dimension dim => Dimension (dim :^ power) where
-  type DimFactorsOf (dim :^ power) = (DimFactorsOf dim) @* power
+  type DimFactorsOf (dim :^ power)
+    = Normalize ((DimFactorsOf dim) @* power)
 
 instance (Unit unit, SingI power) => Unit (unit :^ power) where
   type BaseUnit (unit :^ power) = Canonical
   type DimOfUnit (unit :^ power) = DimOfUnit unit :^ power
   conversionRatio _ = undefined
 
-  type UnitFactorsOf (unit :^ power) = (UnitFactorsOf unit) @* power
+  type UnitFactorsOf (unit :^ power)
+    = Normalize ((UnitFactorsOf unit) @* power)
   canonicalConvRatio _ = canonicalConvRatio (undefined :: unit) ^^ (szToInt (sing :: Sing power))
 
 type instance DefaultUnitOfDim (d :^ z) = DefaultUnitOfDim d :^ z
 
+instance (Show u1, SingI power) => Show (u1 :^ (power :: Z)) where
+  show _ = show (undefined :: u1) ++ "^" ++ show (szToInt (sing :: Sing power))
+
+
 infixr 9 :@
 -- | Multiply a conversion ratio by some constant. Used for defining prefixes.
 data prefix :@ unit = prefix :@ unit
@@ -95,3 +117,5 @@
   type BaseUnit (prefix :@ unit) = unit
   conversionRatio _ = multiplier (undefined :: prefix)
 
+instance (Show prefix, Show unit) => Show (prefix :@ unit) where
+  show _ = show (undefined :: prefix) ++ show (undefined :: unit)
diff --git a/Data/Metrology/Dimensions.hs b/Data/Metrology/Dimensions.hs
--- a/Data/Metrology/Dimensions.hs
+++ b/Data/Metrology/Dimensions.hs
@@ -2,7 +2,7 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file defines the class Dimension, which is needed for
    defining dimensions.
@@ -10,20 +10,17 @@
 
 {-# LANGUAGE TypeFamilies, DataKinds, DefaultSignatures, MultiParamTypeClasses,
              ConstraintKinds, UndecidableInstances, FlexibleContexts,
-             FlexibleInstances, ScopedTypeVariables, TypeOperators #-}
+             FlexibleInstances, ScopedTypeVariables, TypeOperators, CPP #-}
 
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 module Data.Metrology.Dimensions where
 
 import Data.Metrology.Z
 import Data.Metrology.Factor
-import Data.Metrology.LCSU
-import Data.Type.Bool
-import Data.Type.Equality
-import Data.Proxy
-import Data.Singletons
-import GHC.Exts
 
-
 -- | This class is used to mark abstract dimensions, such as @Length@, or
 -- @Mass@.
 class Dimension dim where
@@ -31,4 +28,3 @@
   -- the default of this type family should not be necessary in user code.
   type DimFactorsOf dim :: [Factor *]
   type DimFactorsOf dim = '[F dim One]
-  
diff --git a/Data/Metrology/Factor.hs b/Data/Metrology/Factor.hs
--- a/Data/Metrology/Factor.hs
+++ b/Data/Metrology/Factor.hs
@@ -2,23 +2,36 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file defines the Factor kind and operations over lists of Factors.
 
    Factors represents dimensions and units raised to a power of integers, and the lists of Factors represents monomials of dimensions and units.
 -}
 
-{-# LANGUAGE TypeFamilies, DataKinds, TypeOperators, UndecidableInstances #-}
+{-# LANGUAGE TypeFamilies, DataKinds, TypeOperators, UndecidableInstances, CPP #-}
 
+-- allow compilation even without Cabal
+#ifndef MIN_VERSION_singletons
+#define MIN_VERSION_singletons(a,b,c) 1
+#endif
+
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 module Data.Metrology.Factor where
 
 import GHC.Exts (Constraint)
-import Data.Metrology.Z
-import Data.Type.Equality
+import Data.Metrology.Z as Z
+import Data.Type.Equality as DTE
 import Data.Type.Bool
 
-import Data.Singletons.Tuple (Fst, Snd)
+#if MIN_VERSION_singletons(3,0,0)
+import Prelude.Singletons
+#else
+import Data.Singletons.Prelude
+#endif
 
 -- | This will only be used at the kind level. It holds a dimension or unit
 -- with its exponent.
@@ -55,7 +68,7 @@
 infix 4 $=
 -- | Do these Factors represent the same dimension?
 type family (a :: Factor *) $= (b :: Factor *) :: Bool where
-  (F n1 z1) $= (F n2 z2) = n1 == n2
+  (F n1 z1) $= (F n2 z2) = n1 DTE.== n2
   a         $= b         = False
 
 -- | @(Extract s lst)@ pulls the Factor that matches s out of lst, returning a
@@ -88,8 +101,10 @@
 -- Reorder [] x ==> []
 -- @
 type family Reorder (a :: [Factor *]) (b :: [Factor *]) :: [Factor *] where
-  Reorder x x = x
-  Reorder x '[] = x
+  Reorder x    x     = x
+  Reorder '[]  x     = '[]
+  Reorder '[x] y     = '[x]
+  Reorder x '[]      = x
   Reorder x (h ': t) = Reorder' (Extract h x) t
 
 -- | Helper function in 'Reorder'
@@ -102,7 +117,7 @@
 infix 4 @~
 -- | Check if two @[Factor *]@s should be considered to be equal
 type family (a :: [Factor *]) @~ (b :: [Factor *]) :: Constraint where
-  a @~ b = (Normalize (Reorder a b) ~ Normalize b)
+  a @~ b = (Normalize (a @- b) ~ '[])
 
 ----------------------------------------------------------
 --- Normalization ----------------------------------------
@@ -125,12 +140,12 @@
   '[]                 @@+ b                   = b
   a                   @@+ '[]                 = a
   ((F name z1) ': t1) @@+ ((F name z2) ': t2) = (F name (z1 #+ z2)) ': (t1 @@+ t2)
-  a                   @@+ (h ': t)            = h ': (a @@+ t)
+  (h ': t)            @@+ b                   = h ': (t @@+ b)
 
 infixl 6 @+
--- | Adds corresponding exponents in two dimension
+-- | Adds corresponding exponents in two dimension, preserving order
 type family (a :: [Factor *]) @+ (b :: [Factor *]) :: [Factor *] where
-  a @+ b = (Reorder a b) @@+ b
+  a @+ b = a @@+ (Reorder b a)
 
 infixl 6 @@-
 -- | Subtract exponents in two dimensions, assuming the lists are ordered
@@ -139,16 +154,17 @@
   '[]                 @@- b                   = NegList b
   a                   @@- '[]                 = a
   ((F name z1) ': t1) @@- ((F name z2) ': t2) = (F name (z1 #- z2)) ': (t1 @@- t2)
-  a                   @@- (h ': t)            = (NegDim h) ': (a @@- t)
+  (h ': t)            @@- b                   = h ': (t @@- b)
 
 infixl 6 @-
 -- | Subtract exponents in two dimensions
 type family (a :: [Factor *]) @- (b :: [Factor *]) :: [Factor *] where
-  a @- b = (Reorder a b) @@- b
+  a @- a = '[]
+  a @- b = a @@- (Reorder b a)
 
 -- | negate a single @Factor@
 type family NegDim (a :: Factor *) :: Factor * where
-  NegDim (F n z) = F n (NegZ z)
+  NegDim (F n z) = F n (Z.Negate z)
 
 -- | negate a list of @Factor@s
 type family NegList (a :: [Factor *]) :: [Factor *] where
diff --git a/Data/Metrology/Internal.hs b/Data/Metrology/Internal.hs
--- a/Data/Metrology/Internal.hs
+++ b/Data/Metrology/Internal.hs
@@ -2,7 +2,7 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file gathers and exports user-visible type-level definitions, to
    make error messages less cluttered. Non-expert users should never have
@@ -14,7 +14,7 @@
 -- Module      :  Data.Metrology.Internal
 -- Copyright   :  (C) 2013 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
@@ -54,5 +54,3 @@
 import Data.Metrology.Factor
 import Data.Metrology.Set
 import Data.Metrology.Dimensions
-
-  
diff --git a/Data/Metrology/LCSU.hs b/Data/Metrology/LCSU.hs
--- a/Data/Metrology/LCSU.hs
+++ b/Data/Metrology/LCSU.hs
@@ -2,25 +2,27 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    Defines a locally coherent system of units (LCSUs),
    implemented as an association list.
-   An LCSU is a from base dimensions to units, thus 
+   An LCSU is a from base dimensions to units, thus
    defining a uniquely mapping units for any dimensions.
 -}
 
-{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, UndecidableInstances #-}
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, UndecidableInstances,
+             CPP #-}
 
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 module Data.Metrology.LCSU (
   LCSU(DefaultLCSU), DefaultUnitOfDim,
   Lookup, LookupList, MkLCSU
   ) where
 
 import Data.Metrology.Factor
-import Data.Metrology.Z
-
-import Data.Singletons.Maybe
 
 data LCSU star = MkLCSU_ [(star, star)]
                | DefaultLCSU
diff --git a/Data/Metrology/Linear.hs b/Data/Metrology/Linear.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/Linear.hs
@@ -0,0 +1,319 @@
+{-# LANGUAGE TypeOperators, FlexibleContexts, DataKinds, TypeFamilies,
+             ScopedTypeVariables, ConstraintKinds, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, FlexibleInstances, InstanceSigs, CPP #-}
+
+#if __GLASGOW_HASKELL__ >= 711
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.Linear
+-- Copyright   :  (C) 2014 Richard Eisenberg, (C) 2015 Tobias Markus
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Exports combinators for building quantities out of vectors, from the
+-- @linear@ library.
+------------------------------------------------------------------------------
+
+module Data.Metrology.Linear (
+  -- * Term-level combinators
+
+  -- | The term-level arithmetic operators are defined by
+  -- applying vertical bar(s) to the sides the dimensioned
+  -- quantities acts on.
+
+  -- ** Additive operations
+  zeroV, (|^+^|), (|^-^|), qNegateV, qSumV,
+
+  -- ** Multiplicative operations
+  (|*^|), (|^*|), (|^/|), (*^|), (|^*), (|^/), (|.|),
+
+  -- ** Vector-space operations
+  qBasis, qBasisFor, qScaled, qOuter, qUnit,
+  qQuadrance, qNorm, qSignorm, qProject, qCross,
+
+  -- ** Affine operations
+  (|.-.|), (|.+^|), (|.-^|), qQd, qDistance, qQdA, qDistanceA,
+
+  -- * Nondimensional units, conversion between quantities and numeric values
+  numInV, (^#), quOfV, (^%), showInV,
+  convertV, constantV,
+
+  ) where
+
+import Data.Metrology.Qu
+import Data.Metrology.LCSU
+import Data.Metrology.Validity
+import Data.Metrology.Factor
+import Data.Metrology.Z as Z
+import Data.Metrology.Units
+
+import Linear
+import Linear.Affine hiding (P)
+import qualified Control.Lens as Lens
+
+import Data.Proxy
+import Data.Foldable    as F
+#if __GLASGOW_HASKELL__ < 709
+import Data.Traversable ( Traversable )
+#endif
+
+---------------------------------------
+-- Additive operations
+---------------------------------------
+
+-- | The number 0, polymorphic in its dimension. Use of this will
+-- often require a type annotation.
+zeroV :: (Additive f, Num a) => Qu d l (f a)
+zeroV = Qu Linear.zero
+
+infixl 6 |^+^|
+-- | Add two compatible vector quantities
+(|^+^|) :: (d1 @~ d2, Additive f, Num a)
+        => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (f a)
+(Qu a) |^+^| (Qu b) = Qu (a ^+^ b)
+
+-- | Negate a vector quantity
+qNegateV :: (Additive f, Num a) => Qu d l (f a) -> Qu d l (f a)
+qNegateV (Qu x) = Qu (negated x)
+
+infixl 6 |^-^|
+-- | Subtract two compatible quantities
+(|^-^|) :: (d1 @~ d2, Additive f, Num a)
+        => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (f a)
+(Qu a) |^-^| (Qu b) = Qu (a ^-^ b)
+
+-- | Take the sum of a list of quantities
+qSumV :: (Foldable t, Additive f, Num a) => t (Qu d l (f a)) -> Qu d l (f a)
+qSumV = F.foldr (|^+^|) zeroV
+
+---------------------------------------
+-- Multiplicative operations
+---------------------------------------
+
+infixl 7 |*^|, |^*|, |^/|
+-- | Multiply a scalar quantity by a vector quantity
+(|*^|) :: (Functor f, Num a)
+       => Qu d1 l a -> Qu d2 l (f a) -> Qu (Normalize (d1 @+ d2)) l (f a)
+(Qu a) |*^| (Qu b) = Qu (a *^ b)
+
+-- | Multiply a vector quantity by a scalar quantity
+(|^*|) :: (Functor f, Num a)
+       => Qu d1 l (f a) -> Qu d2 l a -> Qu (Normalize (d1 @+ d2)) l (f a)
+(Qu a) |^*| (Qu b) = Qu (a ^* b)
+
+-- | Divide a vector quantity by a scalar quantity
+(|^/|) :: (Functor f, Fractional a)
+       => Qu d1 l (f a) -> Qu d2 l a -> Qu (Normalize (d1 @- d2)) l (f a)
+(Qu a) |^/| (Qu b) = Qu (a ^/ b)
+
+infixl 7 |^/
+-- | Divide a quantity by a plain old number
+(|^/) :: (Functor f, Fractional a) => Qu d l (f a) -> a -> Qu d l (f a)
+(Qu a) |^/ b = Qu (a ^/ b)
+
+infixl 7 *^| , |^*
+-- | Multiply a quantity by a plain old number from the left
+(*^|) :: (Functor f, Num a) => a -> Qu b l (f a) -> Qu b l (f a)
+a *^| (Qu b) =  Qu (a *^ b)
+
+-- | Multiply a quantity by a plain old number from the right
+(|^*) :: (Functor f, Num a) => Qu b l (f a) -> a -> Qu b l (f a)
+(Qu a) |^* b = Qu (a ^* b)
+
+---------------------------------------
+-- Vector-space operations
+---------------------------------------
+
+-- | Return a default basis, where each basis element measures 1 of the
+-- unit provided.
+qBasis :: ( ValidDLU dim lcsu unit
+          , Additive f
+          , Traversable f
+          , Fractional a )
+       => unit -> [Qu dim lcsu (f a)]
+qBasis u = map (^% u) basis
+
+-- | Return a default basis for the vector space provided. Each basis
+-- element measures 1 of the unit provided.
+qBasisFor :: ( ValidDLU dim lcsu unit
+             , Additive f
+             , Traversable f
+             , Fractional a )
+          => unit -> Qu dim lcsu (f b) -> [Qu dim lcsu (f a)]
+qBasisFor u (Qu vec) = map (^% u) (basisFor vec)
+
+-- | Produce a diagonal (scale) matrix from a vector
+qScaled :: (Traversable f, Num a)
+        => Qu dim lcsu (f a) -> Qu dim lcsu (f (f a))
+qScaled (Qu vec) = Qu (scaled vec)
+
+-- | Outer (tensor) product of two quantity vectors
+qOuter :: (Functor f, Functor g, Num a)
+       => Qu d1 l (f a) -> Qu d2 l (g a) -> Qu (Normalize (d1 @+ d2)) l (f (g a))
+qOuter (Qu a) (Qu b) = Qu (a `outer` b)
+
+-- | Create a unit vector from a setter and a choice of unit.
+qUnit :: (ValidDLU dim lcsu unit, Additive t, Fractional a)
+      => Lens.ASetter' (t a) a -> unit -> Qu dim lcsu (t a)
+qUnit setter u = unit setter ^% u
+
+infixl 7 |.|
+-- | Take a inner (dot) product between two quantities.
+(|.|) :: (Metric f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (Normalize (d1 @+ d2)) l a
+(Qu a) |.| (Qu b) = Qu (a `dot` b)
+
+-- | Square the length of a vector.
+qQuadrance :: (Metric f, Num a) => Qu d l (f a) -> Qu (d @* Z.Two) l a
+qQuadrance (Qu x) = Qu (quadrance x)
+
+-- | Length of a vector.
+qNorm :: (Metric f, Floating a) => Qu d l (f a) -> Qu d l a
+qNorm (Qu x) = Qu (norm x)
+
+-- | Vector in same direction as given one but with length of one. If given the zero
+-- vector, then return it. The returned vector is dimensionless.
+qSignorm :: (Metric f, Floating a)
+         => Qu d l (f a) -> Qu '[] l (f a)
+qSignorm (Qu x) = Qu (signorm x)
+
+-- | @qProject u v@ computes the projection of @v@ onto @u@.
+qProject :: (Metric f, Fractional a)
+         => Qu d2 l (f a) -> Qu d1 l (f a) -> Qu d1 l (f a)
+qProject (Qu u) (Qu v) = Qu (u `project` v)
+
+-- | Cross product of 3D vectors.
+qCross :: Num a
+       => Qu d1 l (V3 a) -> Qu d2 l (V3 a) -> Qu (Normalize (d1 @+ d2)) l (V3 a)
+qCross (Qu x) (Qu y) = Qu (x `cross` y)
+
+-- | Square of the distance between two vectors.
+qQd :: (d1 @~ d2, Metric f, Metric (Diff f), Num a)
+            => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (d1 @* Z.Two) l a
+qQd (Qu a) (Qu b) = Qu (a `qd` b)
+
+-- | Distance between two vectors.
+qDistance :: (d1 @~ d2, Metric f, Metric (Diff f), Floating a)
+          => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l a
+qDistance (Qu a) (Qu b) = Qu (a `distance` b)
+
+---------------------------------------
+-- Affine space operations
+---------------------------------------
+
+-- | Subtract point quantities.
+(|.-.|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (Diff f a)
+(Qu a) |.-.| (Qu b) = Qu (a .-. b)
+
+-- | Add a point to a vector.
+(|.+^|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (Diff f a) -> Qu d1 l (f a)
+(Qu a) |.+^| (Qu b) = Qu (a .+^ b)
+
+-- | Subract a vector from a point.
+(|.-^|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (Diff f a) -> Qu d1 l (f a)
+(Qu a) |.-^| (Qu b) = Qu (a .-^ b)
+
+-- | Square of the distance between two points.
+qQdA :: (d1 @~ d2, Affine f, Foldable (Diff f), Num a)
+            => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (d1 @* Z.Two) l a
+qQdA (Qu a) (Qu b) = Qu (a `qdA` b)
+
+-- | Distance between two points.
+qDistanceA :: (d1 @~ d2, Affine f, Foldable (Diff f), Floating a)
+          => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l a
+qDistanceA (Qu a) (Qu b) = Qu (a `distanceA` b)
+
+---------------------------------------
+-- Top-level operations
+---------------------------------------
+
+-- | Extracts a numerical value from a dimensioned quantity, expressed in
+--   the given unit. For example:
+--
+--   > inMeters :: Length -> Double
+--   > inMeters x = numIn x Meter
+--
+--   or
+--
+--   > inMeters x = x # Meter
+numInV :: forall unit dim lcsu f a.
+         ( ValidDLU dim lcsu unit
+         , Functor f
+         , Fractional a )
+      => Qu dim lcsu (f a) -> unit -> (f a)
+numInV (Qu val) u
+  = val ^* fromRational
+             (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))
+              / canonicalConvRatio u)
+
+infix 5 ^#
+-- | Infix synonym for 'numIn'
+(^#) :: ( ValidDLU dim lcsu unit
+         , Functor f
+         , Fractional a )
+    => Qu dim lcsu (f a) -> unit -> (f a)
+(^#) = numInV
+
+-- | Creates a dimensioned quantity in the given unit. For example:
+--
+--   > height :: Length
+--   > height = quOf 2.0 Meter
+--
+--   or
+--
+--   > height = 2.0 % Meter
+quOfV :: forall unit dim lcsu f a.
+         ( ValidDLU dim lcsu unit
+         , Functor f
+         , Fractional a )
+      => (f a) -> unit -> Qu dim lcsu (f a)
+quOfV d u
+  = Qu (d ^* fromRational
+               (canonicalConvRatio u
+                / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))))
+
+infixr 9 ^%
+-- | Infix synonym for 'quOf'
+(^%) :: ( ValidDLU dim lcsu unit
+         , Functor f
+         , Fractional a )
+    => (f a) -> unit -> Qu dim lcsu (f a)
+(^%) = quOfV
+
+-- | Dimension-keeping cast between different CSUs.
+convertV :: forall d l1 l2 f a.
+  ( ConvertibleLCSUs d l1 l2
+  , Functor f
+  , Fractional a )
+  => Qu d l1 (f a) -> Qu d l2 (f a)
+convertV (Qu x) = Qu $ x ^* fromRational (
+  canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l1))
+  / canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l2)))
+
+
+-- | Compute the argument in the @DefaultLCSU@, and present the result as
+-- lcsu-polymorphic dimension-polymorphic value. Named 'constant' because one
+-- of its dominant usecase is to inject constant quantities into
+-- dimension-polymorphic expressions.
+constantV :: ( d @~ e
+            , ConvertibleLCSUs e DefaultLCSU l
+            , Functor f
+            , Fractional a )
+         => Qu d DefaultLCSU (f a) -> Qu e l (f a)
+constantV = convertV . redim
+
+infix 1 `showInV`
+-- | Show a dimensioned quantity in a given unit. (The default @Show@
+-- instance always uses units as specified in the LCSU.)
+showInV :: ( ValidDLU dim lcsu unit
+          , Functor f
+          , Fractional a
+          , Show unit
+          , Show a
+          , Show (f a) )
+       => Qu dim lcsu (f a) -> unit -> String
+showInV x u = show (x ^# u) ++ " " ++ show u
diff --git a/Data/Metrology/Parser.hs b/Data/Metrology/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/Parser.hs
@@ -0,0 +1,209 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.Parser
+-- Copyright   :  (C) 2014 Richard Eisenberg
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- This module exports functions allowing users to create their own unit
+-- quasiquoters to make for compact unit expressions.
+--
+-- A typical use case is this:
+--
+-- > $(makeQuasiQuoter "unit" [''Kilo, ''Milli] [''Meter, ''Second])
+--
+-- and then, /in a separate module/ (due to GHC's staging constraints)
+--
+-- > x = 3 % [unit| m/s^2 ]
+--
+-- The unit expressions can refer to the prefixes and units specified in
+-- the call to 'makeQuasiQuoter'. The spellings of the prefixes and units
+-- are taken from their @Show@ instances.
+--
+-- The syntax for these expressions is like
+-- F#'s. There are four arithmetic operators (@*@, @/@, @^@, and juxtaposition).
+-- Exponentiation binds the tightest, and it allows an integer to its right
+-- (possibly with minus signs and parentheses). Next tightest is juxtaposition,
+-- which indicates multiplication. Because juxtaposition binds tighter than division,
+-- the expressions @m/s^2@ and @m/s s@ are equivalent. Multiplication and
+-- division bind the loosest and are left-associative, meaning that @m/s*s@
+-- is equivalent to @(m/s)*s@, probably not what you meant. Parentheses in
+-- unit expressions are allowed, of course.
+--
+-- Within a unit string (that is, a unit with an optional prefix), there may
+-- be ambiguity. If a unit string can be interpreted as a unit without a
+-- prefix, that parsing is preferred. Thus, @min@ would be minutes, not
+-- milli-inches (assuming appropriate prefixes and units available.) There still
+-- may be ambiguity between unit strings, even interpreting the string as a prefix
+-- and a base unit. If a unit string is amiguous in this way, it is rejected.
+-- For example, if we have prefixes @da@ and @d@ and units @m@ and @am@, then
+-- @dam@ is ambiguous like this.
+-----------------------------------------------------------------------------
+
+{-# LANGUAGE TemplateHaskell, CPP #-}
+{-# OPTIONS_HADDOCK prune #-}
+
+module Data.Metrology.Parser (
+  -- * Quasiquoting interface
+  makeQuasiQuoter, allUnits, allPrefixes,
+
+  -- * Direct interface
+
+  -- | The definitions below allow users to access the unit parser directly.
+  -- The parser produces 'UnitExp's which can then be further processed as
+  -- necessary.
+  parseUnit,
+  UnitExp(..), SymbolTable,
+  mkSymbolTable,
+
+  -- for internal use only
+  parseUnitExp, parseUnitType
+  ) where
+
+import Prelude hiding ( exp )
+
+import Language.Haskell.TH hiding ( Pred )
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH.Desugar.Lift  ()  -- get the Lift Name instance
+import Data.Maybe
+import Control.Monad
+
+import Text.Parse.Units
+import Data.Metrology
+import Data.Metrology.TH
+
+----------------------------------------------------------------------
+-- TH conversions
+----------------------------------------------------------------------
+
+parseUnitExp :: SymbolTable Name Name -> String -> Either String Exp
+parseUnitExp tab s = to_exp `liftM` parseUnit tab s   -- the Either monad
+  where
+    to_exp Unity                  = ConE 'Number
+    to_exp (Unit (Just pre) unit) = ConE '(:@) `AppE` of_type pre `AppE` of_type unit
+    to_exp (Unit Nothing unit)    = of_type unit
+    to_exp (Mult e1 e2)           = ConE '(:*) `AppE` to_exp e1 `AppE` to_exp e2
+    to_exp (Div e1 e2)            = ConE '(:/) `AppE` to_exp e1 `AppE` to_exp e2
+    to_exp (Pow e i)              = ConE '(:^) `AppE` to_exp e `AppE` mk_sing i
+
+    of_type :: Name -> Exp
+    of_type n = (VarE 'undefined) `SigE` (ConT n)
+
+    mk_sing :: Integer -> Exp
+    mk_sing n
+      | n < 0     = VarE 'sPred `AppE` mk_sing (n + 1)
+      | n > 0     = VarE 'sSucc `AppE` mk_sing (n - 1)
+      | otherwise = VarE 'sZero
+
+parseUnitType :: SymbolTable Name Name -> String -> Either String Type
+parseUnitType tab s = to_type `liftM` parseUnit tab s   -- the Either monad
+  where
+    to_type Unity                  = ConT ''Number
+    to_type (Unit (Just pre) unit) = ConT ''(:@) `AppT` ConT pre `AppT` ConT unit
+    to_type (Unit Nothing unit)    = ConT unit
+    to_type (Mult e1 e2)           = ConT ''(:*) `AppT` to_type e1 `AppT` to_type e2
+    to_type (Div e1 e2)            = ConT ''(:/) `AppT` to_type e1 `AppT` to_type e2
+    to_type (Pow e i)              = ConT ''(:^) `AppT` to_type e `AppT` mk_z i
+
+    mk_z :: Integer -> Type
+    mk_z n
+      | n < 0     = ConT ''Pred `AppT` mk_z (n + 1)
+      | n > 0     = ConT ''Succ `AppT` mk_z (n - 1)
+      | otherwise = ConT 'Zero   -- single quote as it's a data constructor!
+
+----------------------------------------------------------------------
+-- QuasiQuoters
+----------------------------------------------------------------------
+
+emptyQQ :: QuasiQuoter
+emptyQQ = QuasiQuoter { quoteExp = \_ -> fail "No quasi-quoter for expressions"
+                      , quotePat = \_ -> fail "No quasi-quoter for patterns"
+                      , quoteType = \_ -> fail "No quasi-quoter for types"
+                      , quoteDec = \_ -> fail "No quasi-quoter for declarations" }
+
+errorQQ :: String -> QuasiQuoter
+errorQQ msg = QuasiQuoter { quoteExp = \_ -> fail msg
+                          , quotePat = \_ -> fail msg
+                          , quoteType = \_ -> fail msg
+                          , quoteDec = \_ -> fail msg }
+
+-- | @makeQuasiQuoter "qq" prefixes units@ makes a quasi-quoter named @qq@
+-- that considers the prefixes and units provided. These are provided via
+-- names of the /type/ constructors, /not/ the data constructors. See the
+-- module documentation for more info and an example.
+makeQuasiQuoter :: String -> [Name] -> [Name] -> Q [Dec]
+makeQuasiQuoter qq_name_str prefix_names unit_names = do
+  mapM_ checkIsType prefix_names
+  mapM_ checkIsType unit_names
+  qq <- [| case $sym_tab of
+            Left err -> errorQQ err
+            Right computed_sym_tab ->
+              emptyQQ { quoteExp = \unit_exp ->
+                         case parseUnitExp computed_sym_tab unit_exp of
+                           Left err2 -> fail err2
+                           Right exp -> return exp
+                      , quoteType = \unit_exp ->
+                         case parseUnitType computed_sym_tab unit_exp of
+                           Left err2 -> fail err2
+                           Right typ -> return typ
+                      } |]
+  return [ SigD qq_name (ConT ''QuasiQuoter)
+         , ValD (VarP qq_name) (NormalB qq) []]
+  where
+    qq_name = mkName qq_name_str
+
+    mk_pair :: Name -> Q Exp   -- Exp is of type (String, Name)
+    mk_pair n = [| (show (undefined :: $( return $ ConT n )), n) |]
+
+    sym_tab :: Q Exp           -- Exp is of type (Either String SymbolTable)
+    sym_tab = do
+      prefix_pairs <- mapM mk_pair prefix_names
+      unit_pairs   <- mapM mk_pair unit_names
+      [| mkSymbolTable $( return $ ListE prefix_pairs ) $( return $ ListE unit_pairs ) |]
+
+----------------------------------------------------------------------
+-- Getting instances
+----------------------------------------------------------------------
+
+getInstanceNames :: Name -> Q [Name]
+getInstanceNames class_name = do
+  ClassI _ insts <- reify class_name
+  m_names <- forM insts $ \inst ->
+    case inst of
+      InstanceD
+#if __GLASGOW_HASKELL__ >= 711
+        _
+#endif
+          _ ((ConT class_name') `AppT` (ConT unit_name)) []
+        |  class_name == class_name'
+        -> do show_insts <- reifyInstances ''Show [ConT unit_name]
+              case show_insts of
+                [_show_inst] -> return $ Just unit_name
+                _            -> return Nothing
+      _ -> return Nothing
+  return $ catMaybes m_names
+
+#if __GLASGOW_HASKELL__ < 709
+{-# WARNING allUnits, allPrefixes "Retrieving the list of all units and prefixes in scope does not work under GHC 7.8.*. Please upgrade GHC to use these functions." #-}
+#endif
+
+-- | Gets a list of the names of all units with @Show@ instances in scope.
+-- Example usage:
+--
+-- > $( do units <- allUnits
+-- >       makeQuasiQuoter "unit" [] units )
+--
+allUnits :: Q [Name]
+allUnits = getInstanceNames ''Unit
+
+-- | Gets a list of the names of all unit prefixes with @Show@ instances in
+-- scope. Example usage:
+--
+-- > $( do units    <- allUnits
+-- >       prefixes <- allPrefixes
+-- >       makeQuasiQuoter "unit" prefixes units )
+--
+allPrefixes :: Q [Name]
+allPrefixes = getInstanceNames ''UnitPrefix
diff --git a/Data/Metrology/Poly.hs b/Data/Metrology/Poly.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/Poly.hs
@@ -0,0 +1,245 @@
+{- Data/Metrology/Poly.hs
+
+   The units Package
+   Copyright (c) 2013 Richard Eisenberg
+   rae@cs.brynmawr.edu
+
+   This file gathers and exports all user-visible pieces of the units package.
+   It also defines the main creators and consumers of dimensioned objects.
+-}
+
+{-# LANGUAGE ExplicitNamespaces, DataKinds, FlexibleInstances, TypeFamilies,
+             TypeOperators, ConstraintKinds, ScopedTypeVariables,
+             FlexibleContexts, UndecidableInstances, CPP #-}
+
+#if __GLASGOW_HASKELL__ >= 711
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.Poly
+-- Copyright   :  (C) 2013 Richard Eisenberg
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- This module exports all the gubbins needed for type-checking your
+-- dimensioned quantities. See 'Data.Metrology' for some functions
+-- restricted to using a default LCSU, which is suitable for many
+-- applications. See also 'Data.Metrology.Vector' for polymorphic
+-- functions suitable for use with the numerical classes from the
+-- @vector-space@ package.
+-----------------------------------------------------------------------------
+
+module Data.Metrology.Poly (
+  -- * Term-level combinators
+
+  -- | The term-level arithmetic operators are defined by
+  -- applying vertical bar(s) to the sides the dimensioned
+  -- quantities acts on.
+
+  -- ** Additive operations
+  zero, (|+|), (|-|), qSum, qNegate,
+
+  -- ** Multiplicative operations between quantities
+  (|*|), (|/|),
+
+  -- ** Multiplicative operations between a quantity and a non-quantity
+  (*|), (|*), (/|), (|/),
+
+  -- ** Exponentiation
+  (|^), (|^^), qNthRoot,
+  qSq, qCube, qSqrt, qCubeRoot,
+
+  -- ** Comparison
+  qCompare, (|<|), (|>|), (|<=|), (|>=|), (|==|), (|/=|),
+  qApprox, qNapprox,
+
+  -- * Nondimensional units, conversion between quantities and numeric values
+  numIn, (#), quOf, (%), showIn,
+  unity, redim, convert,
+  defaultLCSU, constant,
+
+  -- * Type-level unit combinators
+  (:*)(..), (:/)(..), (:^)(..), (:@)(..),
+  UnitPrefix(..),
+
+  -- * Type-level quantity combinators
+  type (%*), type (%/), type (%^),
+
+  -- * Creating quantity types
+  Qu, MkQu_D, MkQu_DLN, MkQu_U, MkQu_ULN,
+
+  -- * Creating new dimensions
+  Dimension,
+
+  -- * Creating new units
+  Unit(type BaseUnit, type DimOfUnit, conversionRatio),
+  Canonical,
+
+  -- * Numbers, the only built-in unit
+  Dimensionless(..), Number(..), Count, quantity,
+
+  -- * LCSUs (locally coherent system of units)
+  MkLCSU, LCSU(DefaultLCSU), DefaultUnitOfDim,
+
+  -- * Validity checks and assertions
+  CompatibleUnit, CompatibleDim, ConvertibleLCSUs_D,
+  DefaultConvertibleLCSU_D, DefaultConvertibleLCSU_U,
+  MultDimFactors, MultUnitFactors, UnitOfDimFactors,
+
+  -- * Type-level integers
+  Z(..), Succ, Pred, type (#+), type (#-), type (#*), type (#/), Negate,
+
+  -- ** Synonyms for small numbers
+  One, Two, Three, Four, Five, MOne, MTwo, MThree, MFour, MFive,
+
+  -- ** Term-level singletons
+  sZero, sOne, sTwo, sThree, sFour, sFive,
+  sMOne, sMTwo, sMThree, sMFour, sMFive,
+  sSucc, sPred, sNegate,
+
+  -- ** Deprecated synonyms for the ones above
+  pZero, pOne, pTwo, pThree, pFour, pFive,
+  pMOne, pMTwo, pMThree, pMFour, pMFive,
+  pSucc, pPred,
+
+  -- * Internal definitions
+  -- | The following module is re-exported solely to prevent noise in error messages;
+  -- we do not recommend trying to use these definitions in user code.
+  module Data.Metrology.Internal
+
+  ) where
+
+import Data.Metrology.Z
+import Data.Metrology.Qu
+import Data.Metrology.Dimensions
+import Data.Metrology.Factor
+import Data.Metrology.Units
+import Data.Metrology.Combinators
+import Data.Metrology.LCSU
+import Data.Metrology.Validity
+import Data.Metrology.Internal
+
+import Data.Foldable as F
+import Data.Proxy
+
+-- | Extracts a numerical value from a dimensioned quantity, expressed in
+--   the given unit. For example:
+--
+--   > inMeters :: Length -> Double
+--   > inMeters x = numIn x Meter
+--
+--   or
+--
+--   > inMeters x = x # Meter
+numIn :: forall unit dim lcsu n.
+         ( ValidDLU dim lcsu unit
+         , Fractional n )
+      => Qu dim lcsu n -> unit -> n
+numIn (Qu val) u
+  = val * fromRational
+             (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))
+              / canonicalConvRatio u)
+
+infix 5 #
+-- | Infix synonym for 'numIn'
+(#) :: ( ValidDLU dim lcsu unit
+       , Fractional n )
+    => Qu dim lcsu n -> unit -> n
+(#) = numIn
+
+-- | Creates a dimensioned quantity in the given unit. For example:
+--
+--   > height :: Length
+--   > height = quOf 2.0 Meter
+--
+--   or
+--
+--   > height = 2.0 % Meter
+quOf :: forall unit dim lcsu n.
+         ( ValidDLU dim lcsu unit
+         , Fractional n )
+      => n -> unit -> Qu dim lcsu n
+quOf d u
+  = Qu (d * fromRational
+               (canonicalConvRatio u
+                / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))))
+
+infix 5 %
+-- | Infix synonym for 'quOf'
+(%) :: ( ValidDLU dim lcsu unit
+       , Fractional n )
+    => n -> unit -> Qu dim lcsu n
+(%) = quOf
+
+infix 1 `showIn`
+-- | Show a dimensioned quantity in a given unit. (The default @Show@
+-- instance always uses units as specified in the LCSU.)
+showIn :: ( ValidDLU dim lcsu unit
+          , Fractional n
+          , Show unit
+          , Show n )
+       => Qu dim lcsu n -> unit -> String
+showIn x u = show (x # u) ++ " " ++ show u
+
+-- | Dimension-keeping cast between different CSUs.
+convert :: forall d l1 l2 n.
+  ( ConvertibleLCSUs d l1 l2
+  , Fractional n )
+  => Qu d l1 n -> Qu d l2 n
+convert (Qu x) = Qu $ x * fromRational (
+  canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l1))
+  / canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l2)))
+
+-- | Compute the argument in the @DefaultLCSU@, and present the result as
+-- lcsu-polymorphic dimension-polymorphic value. Named 'constant' because one
+-- of its dominant usecase is to inject constant quantities into
+-- dimension-polymorphic expressions.
+constant :: ( d @~ e
+            , ConvertibleLCSUs e DefaultLCSU l
+            , Fractional n )
+         => Qu d DefaultLCSU n -> Qu e l n
+constant = convert . redim
+
+----------------------------------------------------
+-- Qu operations
+----------------------------------------------------
+
+-- | The number 0, polymorphic in its dimension. Use of this will
+-- often require a type annotation.
+zero :: Num n => Qu dimspec l n
+zero = Qu 0
+
+infixl 6 |+|
+-- | Add two compatible quantities
+(|+|) :: (d1 @~ d2, Num n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
+(Qu a) |+| (Qu b) = Qu (a + b)
+
+infixl 6 |-|
+-- | Subtract two compatible quantities
+(|-|) :: (d1 @~ d2, Num n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
+(Qu a) |-| (Qu b) = Qu (a - b)
+
+-- | Take the sum of a list of quantities
+qSum :: (Foldable f, Num n) => f (Qu d l n) -> Qu d l n
+qSum = F.foldr (|+|) zero
+
+-- | Negate a quantity
+qNegate :: Num n => Qu d l n -> Qu d l n
+qNegate (Qu x) = Qu (negate x)
+
+infixl 7 *| , |* , |/
+-- | Multiply a quantity by a scalar from the left
+(*|) :: Num n => n -> Qu b l n -> Qu b l n
+a *| (Qu b) = Qu (a * b)
+
+-- | Multiply a quantity by a scalar from the right
+(|*) :: Num n => Qu a l n -> n -> Qu a l n
+(Qu a) |* b = Qu (a * b)
+
+-- | Divide a quantity by a scalar
+(|/) :: Fractional n => Qu a l n -> n -> Qu a l n
+(Qu a) |/ b = Qu (a / b)
diff --git a/Data/Metrology/Qu.hs b/Data/Metrology/Qu.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/Qu.hs
@@ -0,0 +1,304 @@
+{- Data/Metrology/Qu.hs
+
+   The units Package
+   Copyright (c) 2013 Richard Eisenberg
+   rae@cs.brynmawr.edu
+
+   This file defines the 'Qu' type that represents quantity
+   (a number paired with its measurement reference).
+   This file also defines operations on 'Qu's that are shared between
+   the vector and non-vector interfaces.
+-}
+
+{-# LANGUAGE TypeFamilies, TypeOperators, DataKinds, UndecidableInstances,
+             ConstraintKinds, StandaloneDeriving, GeneralizedNewtypeDeriving,
+             FlexibleInstances, RoleAnnotations, FlexibleContexts,
+             ScopedTypeVariables, CPP #-}
+
+#if __GLASGOW_HASKELL__ >= 711
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+#endif
+
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
+module Data.Metrology.Qu where
+
+import Data.Metrology.Dimensions
+import Data.Metrology.Factor
+import Data.Metrology.Units
+import Data.Metrology.Z
+import Data.Metrology.LCSU
+
+import Control.DeepSeq (NFData (..))
+import Data.VectorSpace
+
+import Text.Read
+import Data.Coerce
+
+-------------------------------------------------------------
+--- Internal ------------------------------------------------
+-------------------------------------------------------------
+
+-- | 'Qu' adds a dimensional annotation to its numerical value type
+-- @n@. This is the representation for all quantities.
+newtype Qu (a :: [Factor *]) (lcsu :: LCSU *) (n :: *) = Qu n
+type role Qu nominal nominal representational
+
+-------------------------------------------------------------
+--- User-facing ---------------------------------------------
+-------------------------------------------------------------
+
+-- Abbreviation for creating a Qu (defined here to avoid a module cycle)
+
+-- | Make a quantity type capable of storing a value of a given
+-- unit. This uses a 'Double' for storage of the value. For example:
+--
+-- > data LengthDim = LengthDim
+-- > instance Dimension LengthDim
+-- > data Meter = Meter
+-- > instance Unit Meter where
+-- >   type BaseUnit Meter = Canonical
+-- >   type DimOfUnit Meter = LengthDim
+-- > type instance DefaultUnitOfDim LengthDim = Meter
+-- > type Length = MkQu_D LengthDim
+--
+-- Note that the dimension /must/ have an instance for the type family
+-- 'DefaultUnitOfDim' for this to work.
+type MkQu_D dim = Qu (DimFactorsOf dim) DefaultLCSU Double
+
+-- | Make a quantity type with a custom numerical type and LCSU.
+type MkQu_DLN dim = Qu (DimFactorsOf dim)
+
+-- | Make a quantity type with a given unit. It will be stored as a 'Double'.
+-- Note that the corresponding dimension /must/ have an appropriate instance
+-- for 'DefaultUnitOfDim' for this to work.
+type MkQu_U unit = Qu (DimFactorsOf (DimOfUnit unit)) DefaultLCSU Double
+
+-- | Make a quantity type with a unit and LCSU with custom numerical type.
+--   The quantity will have the dimension corresponding to the unit.
+type MkQu_ULN unit = Qu (DimFactorsOf (DimOfUnit unit))
+
+---------------------------------------
+---------------------------------------
+-- Privileged operations
+---------------------------------------
+---------------------------------------
+
+---------------------------------------
+-- Quantities of dimension one
+---------------------------------------
+
+-- | Convert a raw number into a unitless dimensioned quantity
+quantity :: n -> Qu '[] l n
+quantity = Qu
+
+---------------------------------------
+-- Multiplicative operations
+---------------------------------------
+
+infixl 7 |*|
+-- | Multiply two quantities
+(|*|) :: Num n => Qu a l n -> Qu b l n -> Qu (Normalize (a @+ b)) l n
+(Qu a) |*| (Qu b) = Qu (a * b)
+
+infixl 7 |/|
+-- | Divide two quantities
+(|/|) :: Fractional n => Qu a l n -> Qu b l n -> Qu (Normalize (a @- b)) l n
+(Qu a) |/| (Qu b) = Qu (a / b)
+
+---------------------------------------
+-- Exponentiation
+---------------------------------------
+
+-- The following are privileged for efficiency.
+
+infixr 8 |^
+-- | Raise a quantity to a integer power, knowing at compile time that the integer is non-negative.
+(|^) :: (NonNegative z, Num n) => Qu a l n -> Sing z -> Qu (a @* z) l n
+(Qu a) |^ sz = Qu (a ^ szToInt sz)
+
+infixr 8 |^^
+-- | Raise a quantity to a integer power known at compile time
+(|^^) :: Fractional n => Qu a l n -> Sing z -> Qu (a @* z) l n
+(Qu a) |^^ sz = Qu (a ^^ szToInt sz)
+
+-- | Take the n'th root of a quantity, where n is known at compile
+-- time
+qNthRoot :: ((Zero < z) ~ True, Floating n)
+        => Sing z -> Qu a l n -> Qu (a @/ z) l n
+qNthRoot sz (Qu a) = Qu (a ** (1.0 / (fromIntegral $ szToInt sz)))
+
+---------------------------------------
+-- Comparison
+---------------------------------------
+
+-- | Compare two quantities
+qCompare :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Ordering
+qCompare (Qu a) (Qu b) = compare a b
+
+infix 4 |<|
+-- | Check if one quantity is less than a compatible one
+(|<|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |<| (Qu b) = a < b
+
+infix 4 |>|
+-- | Check if one quantity is greater than a compatible one
+(|>|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |>| (Qu b) = a > b
+
+infix 4 |<=|
+-- | Check if one quantity is less than or equal to a compatible one
+(|<=|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |<=| (Qu b) = a <= b
+
+infix 4 |>=|
+-- | Check if one quantity is greater than or equal to a compatible one
+(|>=|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |>=| (Qu b) = a >= b
+
+infix 4 |==|
+-- | Check if two quantities are equal (uses the equality of the underlying numerical type)
+(|==|) :: (d1 @~ d2, Eq n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |==| (Qu b) = a == b
+
+infix 4 |/=|
+-- | Check if two quantities are not equal
+(|/=|) :: (d1 @~ d2, Eq n) => Qu d1 l n -> Qu d2 l n -> Bool
+(Qu a) |/=| (Qu b) = a /= b
+
+infix 4 `qApprox` , `qNapprox`
+-- | Compare two compatible quantities for approximate equality. If the
+-- difference between the left hand side and the right hand side arguments are
+-- less than or equal to the /epsilon/, they are considered equal.
+qApprox :: (d0 @~ d1, d0 @~ d2, Num n, Ord n)
+      => Qu d0 l n  -- ^ /epsilon/
+      -> Qu d1 l n  -- ^ left hand side
+      -> Qu d2 l n  -- ^ right hand side
+      -> Bool
+qApprox (Qu epsilon) (Qu a) (Qu b) = abs(a-b) <= epsilon
+
+-- | Compare two compatible quantities for approixmate inequality.
+-- @qNapprox e a b = not $ qApprox e a b@
+qNapprox :: (d0 @~ d1, d0 @~ d2, Num n, Ord n)
+       => Qu d0 l n  -- ^ /epsilon/
+       -> Qu d1 l n  -- ^ left hand side
+       -> Qu d2 l n  -- ^ right hand side
+       -> Bool
+qNapprox (Qu epsilon) (Qu a) (Qu b) = abs(a-b) > epsilon
+
+---------------------------------------
+---------------------------------------
+-- Unprivileged operations
+---------------------------------------
+---------------------------------------
+
+infixl 7 /|
+-- | Divide a scalar by a quantity
+(/|) :: Fractional n => n -> Qu b l n -> Qu (Normalize ('[] @- b)) l n
+a /| b = quantity a |/| b
+
+-- | Square a quantity
+qSq :: Num n => Qu a l n -> Qu (Normalize (a @+ a)) l n
+qSq x = x |*| x
+
+-- | Cube a quantity
+qCube :: Num n => Qu a l n -> Qu (Normalize (Normalize (a @+ a) @+ a)) l n
+qCube x = x |*| x |*| x
+
+-- | Take the square root of a quantity
+qSqrt :: Floating n => Qu a l n -> Qu (a @/ Two) l n
+qSqrt = qNthRoot sTwo
+
+-- | Take the cubic root of a quantity
+qCubeRoot :: Floating n => Qu a l n -> Qu (a @/ Three) l n
+qCubeRoot = qNthRoot sThree
+
+-------------------------------------------------------------
+--- Instances for all quantities ----------------------------
+-------------------------------------------------------------
+
+deriving instance Eq n => Eq (Qu d l n)
+deriving instance Ord n => Ord (Qu d l n)
+deriving instance NFData n => NFData (Qu d l n)
+
+deriving instance AdditiveGroup n => AdditiveGroup (Qu d l n)
+instance VectorSpace n => VectorSpace (Qu d l n) where
+  type Scalar (Qu d l n) = Scalar n
+  a *^ (Qu b) = Qu (a *^ b)
+
+-------------------------------------------------------------
+--- Instances for dimensionless quantities ------------------
+-------------------------------------------------------------
+
+-- Express the condition on `d` via a constraint, so that the
+-- requirement for the Num class can inform the choice of
+-- dimension. See #35.
+deriving instance (d ~ '[], Num n)        => Num (Qu d l n)
+deriving instance (d ~ '[], Real n)       => Real (Qu d l n)
+deriving instance (d ~ '[], Fractional n) => Fractional (Qu d l n)
+deriving instance (d ~ '[], Floating n)   => Floating (Qu d l n)
+deriving instance (d ~ '[], RealFrac n)   => RealFrac (Qu d l n)
+deriving instance (d ~ '[], RealFloat n)  => RealFloat (Qu d l n)
+
+-- But don't do this for Read and Show, because other instances
+-- are indeed sensible. Using the above technique here would make
+-- other instances impossible. Also, note that GeneralizedNewtypeDeriving
+-- puts the "Qu" constructor in Read and Show instances, so don't use
+-- that.
+instance Show n => Show (Qu '[] l n) where
+  showsPrec = coerce (showsPrec :: Int -> n -> ShowS)
+  show      = coerce (show      :: n -> String)
+  showList  = coerce (showList  :: [n] -> ShowS)
+
+instance Read n => Read (Qu '[] l n) where
+  readsPrec    = coerce (readsPrec    :: Int -> ReadS n)
+  readList     = coerce (readList     :: ReadS [n])
+  readPrec     = coerce (readPrec     :: ReadPrec n)
+  readListPrec = coerce (readListPrec :: ReadPrec [n])
+
+-------------------------------------------------------------
+--- Combinators ---------------------------------------------
+-------------------------------------------------------------
+
+infixl 7 %*
+-- | Multiply two quantity types to produce a new one. For example:
+--
+-- > type Velocity = Length %/ Time
+type family (d1 :: *) %* (d2 :: *) :: *
+type instance (Qu d1 l n) %* (Qu d2 l n) = Qu (d1 @+ d2) l n
+
+infixl 7 %/
+-- | Divide two quantity types to produce a new one
+type family (d1 :: *) %/ (d2 :: *) :: *
+type instance (Qu d1 l n) %/ (Qu d2 l n) = Qu (d1 @- d2) l n
+
+infixr 8 %^
+-- | Exponentiate a quantity type to an integer
+type family (d :: *) %^ (z :: Z) :: *
+type instance (Qu d l n) %^ z = Qu (d @* z) l n
+
+-------------------------------------------------------------
+--- Term-level combinators ----------------------------------
+-------------------------------------------------------------
+
+-- | Use this to choose a default LCSU for a dimensioned quantity.
+-- The default LCSU uses the 'DefaultUnitOfDim' representation for each
+-- dimension.
+defaultLCSU :: Qu dim DefaultLCSU n -> Qu dim DefaultLCSU n
+defaultLCSU = id
+
+-- | The number 1, expressed as a unitless dimensioned quantity.
+unity :: Num n => Qu '[] l n
+unity = Qu 1
+
+-- | Cast between equivalent dimension within the same CSU.
+--  for example [kg m s] and [s m kg]. See the README for more info.
+redim :: (d @~ e) => Qu d l n -> Qu e l n
+redim (Qu x) = Qu x
+
+-- | The type of unitless dimensioned quantities.
+-- This is an instance of @Num@, though Haddock doesn't show it.
+-- This is parameterized by an LCSU and a number representation.
+type Count = MkQu_ULN Number
diff --git a/Data/Metrology/Quantity.hs b/Data/Metrology/Quantity.hs
--- a/Data/Metrology/Quantity.hs
+++ b/Data/Metrology/Quantity.hs
@@ -1,223 +1,59 @@
-{- Data/Metrology.Quantity.hs
-
-   The units Package
-   Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.Quantity
+-- Copyright   :  (C) 2014 Richard Eisenberg
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Exports a class 'Quantity' to allow easy conversion between proper
+-- quantities and types from other libraries.
+------------------------------------------------------------------------------
 
-   This file defines the 'Qu' type that represents quantity
-   (a number paired with its measurement reference).
-   This file also defines operations on 'Qu' types.
--}
+{-# LANGUAGE DataKinds, TypeFamilies, ConstraintKinds, UndecidableInstances, CPP #-}
 
-{-# LANGUAGE TypeFamilies, TypeOperators, DataKinds, UndecidableInstances,
-             ConstraintKinds, StandaloneDeriving, GeneralizedNewtypeDeriving,
-             FlexibleInstances, RoleAnnotations #-}
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
 
 module Data.Metrology.Quantity where
 
-import Data.Singletons ( Sing )
-import Data.Metrology.Dimensions
-import Data.Metrology.Factor
-import Data.Metrology.Units
-import Data.Metrology.Z
-import Data.Metrology.LCSU
-
--------------------------------------------------------------
---- Internal ------------------------------------------------
--------------------------------------------------------------
-
--- | 'Qu' adds a dimensional annotation to its numerical value type
--- @n@. This is the representation for all quantities.
-newtype Qu (a :: [Factor *]) (lcsu :: LCSU *) (n :: *) = Qu n
-type role Qu nominal nominal representational
-
--------------------------------------------------------------
---- User-facing ---------------------------------------------
--------------------------------------------------------------
-
--- Abbreviation for creating a Qu (defined here to avoid a module cycle)
+import Data.Metrology.Poly
 
--- | Make a quantity type capable of storing a value of a given
--- unit. This uses a 'Double' for storage of the value. For example:
+-- | 'Quantity' allows for easy conversions in and out of quantities. For example,
+-- say you are working with an outside library for time that defines `UTCTime`, where
+-- that stores the time measured in seconds. You could say
 --
--- > data LengthDim = LengthDim
--- > instance Dimension LengthDim
--- > data Meter = Meter
--- > instance Unit Meter where
--- >   type BaseUnit Meter = Canonical
--- >   type DimOfUnit Meter = LengthDim
--- > type instance DefaultUnitOfDim LengthDim = Meter
--- > type Length = MkQu_D LengthDim
+-- > instance Quantity UTCTime where
+-- >   type QuantityUnit = Second
+-- >   fromQuantity = ...
+-- >   toQuantity = ...
 --
--- Note that the dimension /must/ have an instance for the type family
--- 'DefaultUnitOfDim' for this to work.
-type MkQu_D dim = Qu (DimFactorsOf dim) DefaultLCSU Double
-
--- | Make a quantity type with a custom numerical type and LCSU.
-type MkQu_DLN dim = Qu (DimFactorsOf dim)
-
--- | Make a quantity type with a given unit. It will be stored as a 'Double'.
--- Note that the corresponding dimension /must/ have an appropriate instance
--- for 'DefaultUnitOfDim' for this to work.
-type MkQu_U unit = Qu (DimFactorsOf (DimOfUnit unit)) DefaultLCSU Double
-
--- | Make a quantity type with a unit and LCSU with custom numerical type.
---   The quantity will have the dimension corresponding to the unit.
-type MkQu_ULN unit = Qu (DimFactorsOf (DimOfUnit unit))
-
-
-infixl 6 |+|
--- | Add two compatible quantities
-(|+|) :: (d1 @~ d2, Num n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
-(Qu a) |+| (Qu b) = Qu (a + b)
-
-infixl 6 |-|
--- | Subtract two compatible quantities
-(|-|) :: (d1 @~ d2, Num n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
-(Qu a) |-| (Qu b) = Qu (a - b)
-
-infixl 7 |*|
--- | Multiply two quantities
-(|*|) :: Num n => Qu a l n -> Qu b l n -> Qu (Normalize (a @+ b)) l n
-(Qu a) |*| (Qu b) = Qu (a * b)
-
-infixl 7 |/|
--- | Divide two quantities
-(|/|) :: Fractional n => Qu a l n -> Qu b l n -> Qu (Normalize (a @- b)) l n
-(Qu a) |/| (Qu b) = Qu (a / b)
-
-infixl 7 *| , |* , /| , |/
--- | Multiply a quantity by a scalar from the left
-(*|) :: Num n => n -> Qu b l n -> Qu b l n
-a *| (Qu b) = Qu (a * b)
-
--- | Multiply a quantity by a scalar from the right
-(|*) :: Num n => Qu a l n -> n -> Qu a l n
-(Qu a) |* b = Qu (a * b)
-
--- | Divide a scalar by a quantity
-(/|) :: Fractional n => n -> Qu b l n -> Qu (NegList b) l n
-a /| (Qu b) = Qu (a / b)
-
--- | Divide a quantity by a scalar
-(|/) :: Fractional n => Qu a l n -> n -> Qu a l n
-(Qu a) |/ b = Qu (a / b)
-
-infixr 8 |^
--- | Raise a quantity to a integer power, knowing at compile time that the integer is non-negative.
-(|^) :: Fractional n => Qu a l n -> Sing z -> Qu (a @* z) l n -- TODO: type level proof here
-(Qu a) |^ sz = Qu (a ^ szToInt sz)
-
-infixr 8 |^^
--- | Raise a quantity to a integer power known at compile time
-(|^^) :: Fractional n => Qu a l n -> Sing z -> Qu (a @* z) l n
-(Qu a) |^^ sz = Qu (a ^^ szToInt sz)
-
--- | Take the n'th root of a quantity, where n is known at compile
--- time
-nthRoot :: ((Zero < z) ~ True, Floating n)
-        => Sing z -> Qu a l n -> Qu (a @/ z) l n
-nthRoot sz (Qu a) = Qu (a ** (1.0 / (fromIntegral $ szToInt sz)))
-
-infix 4 |<|
--- | Check if one quantity is less than a compatible one
-(|<|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |<| (Qu b) = a < b
-
-infix 4 |>|
--- | Check if one quantity is greater than a compatible one
-(|>|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |>| (Qu b) = a > b
-
-infix 4 |<=|
--- | Check if one quantity is less than or equal to a compatible one
-(|<=|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |<=| (Qu b) = a <= b
-
-infix 4 |>=|
--- | Check if one quantity is greater than or equal to a compatible one
-(|>=|) :: (d1 @~ d2, Ord n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |>=| (Qu b) = a >= b
-
-infix 4 |==|
--- | Check if two quantities are equal (uses the equality of the underlying numerical type)
-(|==|) :: (d1 @~ d2, Eq n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |==| (Qu b) = a == b
-
-infix 4 |/=|
--- | Check if two quantities are not equal
-(|/=|) :: (d1 @~ d2, Eq n) => Qu d1 l n -> Qu d2 l n -> Bool
-(Qu a) |/=| (Qu b) = a /= b
-
-infix 4 `qApprox` , `qNapprox`
--- | Compare two compatible quantities for approximate equality.  If
--- the difference between the left hand side and the right hand side
--- arguments are less than the /epsilon/, they are considered equal.
-qApprox :: (d0 @~ d1, d0 @~ d2, Num n, Ord n)
-      => Qu d0 l n  -- ^ /epsilon/
-      -> Qu d1 l n  -- ^ left hand side
-      -> Qu d2 l n  -- ^ right hand side
-      -> Bool  
-qApprox (Qu epsilon) (Qu a) (Qu b) = abs(a-b) < epsilon
-
--- | Compare two compatible quantities for approixmate inequality.  
--- @qNapprox e a b = not $ qApprox e a b@
-qNapprox :: (d0 @~ d1, d0 @~ d2, Num n, Ord n)
-       => Qu d0 l n  -- ^ /epsilon/      
-       -> Qu d1 l n  -- ^ left hand side 
-       -> Qu d2 l n  -- ^ right hand side
-       -> Bool
-qNapprox (Qu epsilon) (Qu a) (Qu b) = abs(a-b) >= epsilon
-
--- | Square a quantity
-qSq :: Num n => Qu a l n -> Qu (Normalize (a @+ a)) l n
-qSq x = x |*| x
-
--- | Cube a quantity
-qCube :: Num n => Qu a l n -> Qu (Normalize (Normalize (a @+ a) @+ a)) l n
-qCube x = x |*| x |*| x
-
--- | Take the square root of a quantity
-qSqrt :: Floating n => Qu a l n -> Qu (a @/ Two) l n
-qSqrt = nthRoot pTwo
-
--- | Take the cubic root of a quantity
-qCubeRoot :: Floating n => Qu a l n -> Qu (a @/ Three) l n
-qCubeRoot = nthRoot pThree
-
-
--------------------------------------------------------------
---- Instances for dimensionless quantities ------------------
--------------------------------------------------------------
-
-deriving instance Eq n => Eq (Qu '[] l n)
-deriving instance Ord n => Ord (Qu '[] l n)
-deriving instance Num n => Num (Qu '[] l n)
-deriving instance Real n => Real (Qu '[] l n)
-deriving instance Fractional n => Fractional (Qu '[] l n)
-deriving instance Floating n => Floating (Qu '[] l n)
-deriving instance RealFrac n => RealFrac (Qu '[] l n)
-deriving instance RealFloat n => RealFloat (Qu '[] l n)
+-- Then, conversions are easy and unit-safe.
+class Quantity t where
+  -- | The unit associated with @t@.
+  type QuantityUnit t :: *
 
--------------------------------------------------------------
---- Combinators ---------------------------------------------
--------------------------------------------------------------
+  -- | The LCSU associated with @t@. Defaults to 'DefaultLCSU'.
+  type QuantityLCSU t :: LCSU *
+  type QuantityLCSU t = DefaultLCSU
 
-infixl 7 %*
--- | Multiply two quantity types to produce a new one. For example:
---
--- > type Velocity = Length %/ Time
-type family (d1 :: *) %* (d2 :: *) :: *
-type instance (Qu d1 l n) %* (Qu d2 l n) = Qu (d1 @+ d2) l n
+  -- | The numerical representation associated with @t@. Defaults to 'Double'.
+  type QuantityRep t :: *
+  type QuantityRep t = Double
 
-infixl 7 %/
--- | Divide two quantity types to produce a new one
-type family (d1 :: *) %/ (d2 :: *) :: *
-type instance (Qu d1 l n) %/ (Qu d2 l n) = Qu (d1 @- d2) l n
+  fromQuantity :: QuantityQu t -> t
+  toQuantity :: t -> QuantityQu t
 
-infixr 8 %^
--- | Exponentiate a quantity type to an integer
-type family (d :: *) %^ (z :: Z) :: *
-type instance (Qu d l n) %^ z = Qu (d @* z) l n
+-- | The 'Qu' type associated with a member of the 'Quantity' class
+type QuantityQu t = MkQu_ULN (QuantityUnit t) (QuantityLCSU t) (QuantityRep t)
 
+instance ValidDL d l =>
+         Quantity (Qu d l n) where
+  type QuantityUnit (Qu d l n) = UnitOfDimFactors d l
+  type QuantityLCSU (Qu d l n) = l
+  type QuantityRep  (Qu d l n) = n
 
+  fromQuantity = id
+  toQuantity = id
diff --git a/Data/Metrology/Set.hs b/Data/Metrology/Set.hs
--- a/Data/Metrology/Set.hs
+++ b/Data/Metrology/Set.hs
@@ -2,7 +2,7 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    Defines set-like operations on type-level lists.
 -}
@@ -12,7 +12,7 @@
 -- Module      :  Data.Metrology.Set
 -- Copyright   :  (C) 2013 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
diff --git a/Data/Metrology/Show.hs b/Data/Metrology/Show.hs
--- a/Data/Metrology/Show.hs
+++ b/Data/Metrology/Show.hs
@@ -1,5 +1,15 @@
 {-# LANGUAGE PolyKinds, DataKinds, TypeOperators, FlexibleInstances,
-             ScopedTypeVariables, FlexibleContexts, ConstraintKinds #-}
+             ScopedTypeVariables, FlexibleContexts, ConstraintKinds, CPP,
+             UndecidableInstances #-}
+
+#if __GLASGOW_HASKELL__ < 709
+{-# LANGUAGE OverlappingInstances #-}
+#endif
+
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 -----------------------------------------------------------------------------
@@ -7,28 +17,25 @@
 -- Module      :  Data.Metrology.Show
 -- Copyright   :  (C) 2013 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- This module defines 'Show' instance for quantities. The show instance
--- prints out the number stored internally with its canonical units. To print
+-- prints out the number stored internally with its correct units. To print
 -- out quantities with specific units use the function `showIn`.
 -----------------------------------------------------------------------------
 
-module Data.Metrology.Show (showIn) where
+module Data.Metrology.Show () where
 
 import Data.Proxy (Proxy(..))
 import Data.List
-import Data.Singletons (Sing, sing, SingI)
+import Data.Singletons (sing, SingI)
 
 import Data.Metrology.Factor
-import Data.Metrology.Quantity
+import Data.Metrology.Qu
 import Data.Metrology.Z
 import Data.Metrology.LCSU
-import Data.Metrology.Combinators
-import Data.Metrology.Units
-import Data.Metrology
 
 class ShowUnitFactor (dims :: [Factor *]) where
   showDims :: Bool   -- take absolute value of exponents?
@@ -58,7 +65,7 @@
     case (length nums, length denoms) of
       (0, 0) -> ""
       (_, 0) -> " " ++ nums
-      (0, _) -> build_string (snd (showDims False p))
+      (0, _) -> " " ++ build_string (snd (showDims False p))
       (_, _) -> " " ++ nums ++ "/" ++ denoms
   where
     mapPair :: (a -> b) -> (a, a) -> (b, b)
@@ -74,32 +81,11 @@
     build_string_helper [s] = s
     build_string_helper (h:t) = h ++ " * " ++ build_string_helper t
 
--- enable showing of compound units:
-instance (Show u1, Show u2) => Show (u1 :* u2) where
-  show _ = show (undefined :: u1) ++ " " ++ show (undefined :: u2)
-
-instance (Show u1, Show u2) => Show (u1 :/ u2) where
-  show _ = show (undefined :: u1) ++ "/" ++ show (undefined :: u2)
-
-instance (Show u1, SingI power) => Show (u1 :^ (power :: Z)) where
-  show _ = show (undefined :: u1) ++ "^" ++ show (szToInt (sing :: Sing power))
-
--- enable showing of units with prefixes:
-instance (Show prefix, Show unit) => Show (prefix :@ unit) where
-  show _ = show (undefined :: prefix) ++ show (undefined :: unit)
-
-instance (ShowUnitFactor (LookupList dims lcsu), Show n)
-           => Show (Qu dims lcsu n) where
+instance
+#if __GLASGOW_HASKELL__ >= 709
+    {-# OVERLAPPABLE #-}
+#endif
+    (ShowUnitFactor (LookupList dims lcsu), Show n)
+    => Show (Qu dims lcsu n) where
   show (Qu d) = show d ++
-                (' ' : showFactor (Proxy :: Proxy (LookupList dims lcsu)))
-
-infix 1 `showIn`
-
--- | Show a dimensioned quantity in a given unit. (The default @Show@
--- instance always uses canonical units.)
-showIn :: ( ValidDLU dim lcsu unit
-          , Fractional n
-          , Show unit
-          , Show n )
-       => Qu dim lcsu n -> unit -> String
-showIn x u = show (x # u) ++ " " ++ show u
+                (showFactor (Proxy :: Proxy (LookupList dims lcsu)))
diff --git a/Data/Metrology/TH.hs b/Data/Metrology/TH.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/TH.hs
@@ -0,0 +1,236 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.TH
+-- Copyright   :  (C) 2014 Richard Eisenberg
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- This module exports Template Haskell functions to make working with
+-- @units@ a little more convenient.
+-----------------------------------------------------------------------------
+
+{-# LANGUAGE TemplateHaskell, CPP #-}
+{-# OPTIONS_HADDOCK prune #-}
+
+module Data.Metrology.TH (
+  evalType,
+  declareDimension, declareCanonicalUnit, declareDerivedUnit, declareMonoUnit,
+  declareConstant,
+
+  -- for internal use only
+  checkIsType
+  ) where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Desugar         ( dsType, sweeten )
+import Language.Haskell.TH.Desugar.Expand  ( expandUnsoundly )
+import Language.Haskell.TH.Desugar.Lift ()   -- need Lift Rational
+
+import Data.Metrology.Dimensions
+import Data.Metrology.Units
+import Data.Metrology.LCSU
+import Data.Metrology.Poly
+
+-- | "Evaluates" a type as far as it can. This is useful, say, in instance
+-- declarations:
+--
+-- > instance Show $(evalType [t| Length |]) where ...
+--
+-- Without the 'evalType', the instance declaration fails because @Length@
+-- mentions type families, which can't be used in instance declarations.
+--
+-- This function is somewhat experimental, and will likely not work with
+-- more polymorphic types. (If it doesn't work, not all of the type families
+-- will be evaluated, and the instance declaration will fail. This function
+-- should never cause /incorrect/ behavior.)
+--
+evalType :: Q Type -> Q Type
+evalType qty = do
+  ty <- qty
+  dty <- dsType ty
+  ex_dty <- expandUnsoundly dty
+    -- NB: No units type families branch on kind variables, so this is safe here.
+  return $ sweeten ex_dty
+
+-- Checks to make sure the given name names a /type/, not a /data constructor/.
+-- Reports a compile-time error if the name is not a type.
+checkIsType :: Name -> Q ()
+checkIsType n = do
+  info <- reify n
+  case info of
+    ClassOpI {} -> generic_error
+    DataConI {} -> datacon_error
+    VarI {} -> generic_error
+    _ -> return ()
+  where
+    generic_error = reportError $ "The name " ++ show n ++ " does not describe a type.\n    A type is expected here."
+    datacon_error = reportError $ "The name " ++ show n ++ " describes a data constructor.\n    Did you perhaps mean to say ''" ++ nameBase n ++ "? Note the two quotes."
+
+-- | Declare a new dimension of the given name:
+--
+-- > $(declareDimension "Length")
+--
+-- produces
+--
+-- > data Length = Length
+-- > instance Dimension Length
+declareDimension :: String -> Q [Dec]
+declareDimension str =
+  return [ mkEmptyDataD name
+#if __GLASGOW_HASKELL__ >= 711
+         , InstanceD Nothing [] (ConT ''Dimension `AppT` ConT name) []
+#else
+         , InstanceD [] (ConT ''Dimension `AppT` ConT name) []
+#endif
+         ]
+  where
+    name = mkName str
+
+-- | Conditionally generates a @Show@ instance
+maybeMkShowInstance :: Name -> Maybe String -> Q [Dec]
+maybeMkShowInstance name (Just abbrev) =
+  [d| instance Show $(return $ ConT name) where { show _ = abbrev } |]
+maybeMkShowInstance _ Nothing = return []
+
+-- | @declareCanonicalUnit unit_name dim (Just abbrev)@ creates a new
+-- canonical unit (that is, it is not defined in terms of other known units)
+-- named @unit_name@, measuring dimension @dim@. @abbrev@ will be the
+-- abbreviation in the unit's @Show@ instance. If no abbraviation is supplied,
+-- then no @Show@ instance will be generated.
+--
+-- Example usage:
+--
+-- > $(declareCanonicalUnit "Meter" [t| Length |] (Just "m"))
+declareCanonicalUnit :: String -> Q Type -> Maybe String -> Q [Dec]
+declareCanonicalUnit unit_name_str dim m_abbrev = do
+  show_instance <- maybeMkShowInstance unit_name m_abbrev
+  unit_instance <- [d| instance Unit $unit_type where
+                         type BaseUnit $unit_type = Canonical
+                         type DimOfUnit $unit_type = $dim |]
+  return $ (mkEmptyDataD unit_name)
+           : unit_instance ++ show_instance
+  where
+    unit_name = mkName unit_name_str
+    unit_type = return $ ConT unit_name
+
+-- | @declareDerivedUnit unit_name base_unit_type ratio (Just abbrev)@ creates
+-- a new derived unit, expressed in terms of @base_unit_type@. @ratio@ says
+-- how many base units are in the derived unit. (Thus, if @unit_name@ is
+-- @"Minute"@ and @base_unit_type@ is @''Second@, then @ratio@ would be @60@.)
+-- @abbrev@, if supplied, becomes the string produced in the derived unit's
+-- @Show@ instance. If no abbreviation is supplied, no @Show@ instance is
+-- generated.
+--
+-- Example usage:
+--
+-- > $(declareDerivedUnit "Minute" [t| Second |] 60 (Just "min"))
+declareDerivedUnit :: String -> Q Type -> Rational -> Maybe String -> Q [Dec]
+declareDerivedUnit unit_name_str base_unit ratio m_abbrev = do
+  show_instance <- maybeMkShowInstance unit_name m_abbrev
+  unit_instance <- [d| instance Unit $unit_type where
+                         type BaseUnit $unit_type = $base_unit
+                         conversionRatio _ = ratio |]
+  return $ (mkEmptyDataD unit_name)
+           : unit_instance ++ show_instance
+  where
+    unit_name = mkName unit_name_str
+    unit_type = return $ ConT unit_name
+
+-- | @declareMonoUnit unit_name (Just abbrev)@ creates a new derived unit,
+-- intended for use without unit polymorphism. The same type stands for both
+-- the unit and dimension, and the instance of 'DefaultUnitOfDim' is set up
+-- accordingly. Use this function (with the 'Data.Metrology' imports) if you
+-- don't want to bother with LCSUs and just want to get to work. The @abbrev@,
+-- if supplied, creates an appropriate @Show@ instance.
+--
+-- > $(declareMonoUnit "Meter" (Just "m"))
+--
+-- produces all of the following
+--
+-- > data Meter = Meter
+-- > instance Dimension Meter
+-- > instance Unit Meter where
+-- >   type BaseUnit Meter = Canonical
+-- >   type DimOfUnit Meter = Meter
+-- > type instance DefaultUnitOfDim Meter = Meter
+-- > instance Show Meter where
+-- >   show _ = "m"
+--
+-- After a declaration like this, you probably want
+--
+-- > type Length = MkQu_U Meter
+--
+-- This last line is /not/ generated, as it is easy enough for you to write,
+-- and it involves a new name (@Length@).
+declareMonoUnit :: String -> Maybe String -> Q [Dec]
+declareMonoUnit unit_name_str m_abbrev = do
+  show_instance <- maybeMkShowInstance unit_name m_abbrev
+  dim_instance <- [d| instance Dimension $unit_type |]
+  unit_instance <- [d| instance Unit $unit_type where
+                         type BaseUnit $unit_type = Canonical
+                         type DimOfUnit $unit_type = $unit_type |]
+  default_instance <- [d| type instance DefaultUnitOfDim $unit_type = $unit_type |]
+  return $ (mkEmptyDataD unit_name)
+           : show_instance ++ dim_instance ++ unit_instance ++ default_instance
+  where
+    unit_name = mkName unit_name_str
+    unit_type = return $ ConT unit_name
+
+-- | @declareConstant const_name value unit_type@ creates a new numerical
+-- constant, named @const_name@. Its numerical value is @value@ expressed
+-- in units given by @unit_type@. The constant is polymorphic in both its
+-- LCSU and numerical representation. For example,
+--
+-- > declareConstant "gravity_g" 9.80665 [t| Meter :/ Second :^ Two |]
+--
+-- yields
+--
+-- > gravity_g :: forall lcsu n.
+-- >              ( Fractional n
+-- >              , CompatibleUnit lcsu (Meter :/ Second :^ Two) )
+-- >           => MkQu_ULN (Meter :/ Second :^ Two) lcsu n
+-- > gravity_g = 9.80665 % (undefined :: Meter :/ Second :^ Two)
+declareConstant :: String -> Rational -> Q Type -> Q [Dec]
+declareConstant name value q_unit_type = do
+  unit_type <- q_unit_type
+  lcsu_name <- newName "lcsu"
+  n_name <- newName "n"
+  let lcsu = VarT lcsu_name
+      n    = VarT n_name
+      const_name = mkName name
+      const_type =
+#if __GLASGOW_HASKELL__ >= 900
+                   ForallT [PlainTV lcsu_name SpecifiedSpec, PlainTV n_name SpecifiedSpec]
+#else
+                   ForallT [PlainTV lcsu_name, PlainTV n_name]
+#endif
+                           [ mkClassP ''Fractional [n]
+                           , mkClassP ''CompatibleUnit [lcsu, unit_type] ] $
+                   ConT ''MkQu_ULN `AppT` unit_type `AppT` lcsu `AppT` n
+      ty_sig = SigD const_name const_type
+      dec    = ValD (VarP const_name) (NormalB $
+                                       VarE '(%) `AppE` LitE (RationalL value)
+                                                 `AppE` SigE (VarE 'undefined)
+                                                             unit_type) []
+  return [ty_sig, dec]
+  where
+#if __GLASGOW_HASKELL__ < 709
+    mkClassP = ClassP
+#else
+    mkClassP n tys = foldl AppT (ConT n) tys
+#endif
+
+-- Make a DataD like `data <name> = <name>`.
+mkEmptyDataD :: Name -> Dec
+mkEmptyDataD name
+#if __GLASGOW_HASKELL__ >= 801
+  = DataD [] name [] Nothing [con] []
+#elif __GLASGOW_HASKELL__ >= 711
+  = DataD [] name [] Nothing [con] []
+#else
+  = DataD [] name [] [con] []
+#endif
+  where
+    con = NormalC name []
diff --git a/Data/Metrology/Units.hs b/Data/Metrology/Units.hs
--- a/Data/Metrology/Units.hs
+++ b/Data/Metrology/Units.hs
@@ -2,16 +2,24 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file defines the class Unit, which is needed for
    user-defined units.
 -}
 
 {-# LANGUAGE TypeFamilies, DataKinds, DefaultSignatures, MultiParamTypeClasses,
-             ConstraintKinds, UndecidableInstances, FlexibleContexts,
+             ConstraintKinds, UndecidableInstances, FlexibleContexts, CPP,
              FlexibleInstances, ScopedTypeVariables, TypeOperators, PolyKinds #-}
 
+#if __GLASGOW_HASKELL__ >= 711
+{-# LANGUAGE UndecidableSuperClasses #-}
+#endif
+
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 module Data.Metrology.Units where
 
 import Data.Metrology.Z
@@ -128,9 +136,13 @@
 -- Conversion ratios for lists of units
 -----------------------------------------------------------------------
 
+type family Units (dfactors :: [Factor *]) :: Constraint where
+  Units '[]                    = ()
+  Units (F unit z ': dfactors) = (Unit unit, Units dfactors)
+
 -- | Classifies well-formed list of unit factors, and permits calculating a
 -- conversion ratio for the purposes of LCSU conversions.
-class UnitFactor (units :: [Factor *]) where
+class (Units units) => UnitFactor (units :: [Factor *]) where
   canonicalConvRatioSpec :: Proxy units -> Rational
 
 instance UnitFactor '[] where
@@ -140,3 +152,22 @@
   canonicalConvRatioSpec _ =
     (canonicalConvRatio (undefined :: unit) ^^ szToInt (sing :: Sing n)) *
     canonicalConvRatioSpec (Proxy :: Proxy rest)
+
+-------------------------------------------------------------
+--- "Number" unit -------------------------------------------
+-------------------------------------------------------------
+
+-- | The dimension for the dimensionless quantities.
+-- It is also called "quantities of dimension one", but
+-- @One@ is confusing with the type-level integer One.
+data Dimensionless = Dimensionless
+instance Dimension Dimensionless where
+  type DimFactorsOf Dimensionless = '[]
+type instance DefaultUnitOfDim Dimensionless = Number
+
+-- | The unit for unitless dimensioned quantities
+data Number = Number -- the unit for unadorned numbers
+instance Unit Number where
+  type BaseUnit Number = Canonical
+  type DimOfUnit Number = Dimensionless
+  type UnitFactorsOf Number = '[]
diff --git a/Data/Metrology/Unsafe.hs b/Data/Metrology/Unsafe.hs
--- a/Data/Metrology/Unsafe.hs
+++ b/Data/Metrology/Unsafe.hs
@@ -1,23 +1,53 @@
-{-# LANGUAGE Unsafe #-}
+{-# LANGUAGE Unsafe, CPP #-}
 
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Metrology.Unsafe
 -- Copyright   :  (C) 2013 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- This module exports the constructor of the 'Qu' type. This allows you
--- to write code that takes creates and reads quantities at will, 
+-- to write code that takes creates and reads quantities at will,
 -- which may lead to dimension unsafety. Use at your peril.
+--
+-- This module also exports 'UnsafeQu', which is a simple wrapper around
+-- 'Qu' that has 'Functor', etc., instances. The reason 'Qu' itself doesn't
+-- have a 'Functor' instance is that it would be unit-unsafe, allowing you,
+-- say, to add 1 to a quantity.... but 1 what? That's the problem. However,
+-- a 'Functor' instance is likely useful, hence 'UnsafeQu'.
 -----------------------------------------------------------------------------
 
 module Data.Metrology.Unsafe (
-  -- * The 'Dim' type
+  -- * The 'Qu' type
   Qu(..),
+
+  -- * 'UnsafeQu'
+  UnsafeQu(..)
   ) where
 
-import Data.Metrology.Quantity
+import Data.Metrology.Qu
 
+#if __GLASGOW_HASKELL__ < 709
+import Control.Applicative
+import Data.Foldable
+import Data.Traversable
+#endif
+
+-- | A basic wrapper around 'Qu' that has more instances.
+newtype UnsafeQu d l n = UnsafeQu { qu :: Qu d l n }
+
+instance Functor (UnsafeQu d l) where
+  fmap f (UnsafeQu (Qu x)) = UnsafeQu (Qu (f x))
+
+instance Applicative (UnsafeQu d l) where
+  pure x = UnsafeQu (Qu x)
+  UnsafeQu (Qu f) <*> UnsafeQu (Qu x) = UnsafeQu (Qu (f x))
+
+instance Foldable (UnsafeQu d l) where
+  foldMap f (UnsafeQu (Qu x)) = f x
+
+instance Traversable (UnsafeQu d l) where
+  traverse f (UnsafeQu (Qu x)) = UnsafeQu . Qu <$> f x
diff --git a/Data/Metrology/Validity.hs b/Data/Metrology/Validity.hs
--- a/Data/Metrology/Validity.hs
+++ b/Data/Metrology/Validity.hs
@@ -2,14 +2,18 @@
 
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file defines validity checks on dimension, unit, and LCSU definitions.
 -}
 
 {-# LANGUAGE TypeFamilies, DataKinds, TypeOperators, PolyKinds,
-             UndecidableInstances #-}
+             UndecidableInstances, CPP #-}
 
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-star-is-type #-}
+#endif
+
 module Data.Metrology.Validity where
 
 import Data.Metrology.LCSU
@@ -17,8 +21,31 @@
 import Data.Metrology.Dimensions
 import Data.Metrology.Units
 import Data.Metrology.Set
+import Data.Metrology.Combinators
 import GHC.Exts ( Constraint )
 
+------------------------------------------------
+-- Helper functions
+------------------------------------------------
+
+-- | Extract a dimension specifier from a list of factors
+type family MultDimFactors (facts :: [Factor *]) where
+  MultDimFactors '[] = Dimensionless
+  MultDimFactors (F d z ': ds) = (d :^ z) :* MultDimFactors ds
+
+-- | Extract a unit specifier from a list of factors
+type family MultUnitFactors (facts :: [Factor *]) where
+  MultUnitFactors '[] = Number
+  MultUnitFactors (F unit z ': units) = (unit :^ z) :* MultUnitFactors units
+
+-- | Extract a unit from a dimension factor list and an LCSU
+type family UnitOfDimFactors (dims :: [Factor *]) (lcsu :: LCSU *) :: * where
+  UnitOfDimFactors dims lcsu = MultUnitFactors (LookupList dims lcsu)
+
+------------------------------------------------
+-- Main validity functions
+------------------------------------------------
+
 -- | Check if a (dimension factors, LCSU, unit) triple are all valid to be used together.
 type family ValidDLU (dfactors :: [Factor *]) (lcsu :: LCSU *) (unit :: *) where
   ValidDLU dfactors lcsu unit =
@@ -30,8 +57,7 @@
 -- | Check if a (dimension factors, LCSU) pair are valid to be used together. This
 -- checks that each dimension maps to a unit of the correct dimension.
 type family ValidDL (dfactors :: [Factor *]) (lcsu :: LCSU *) :: Constraint where
-  ValidDL '[] lcsu             = (() :: Constraint)
-  ValidDL (F d z ': rest) lcsu = (DimOfUnit (Lookup d lcsu) ~ d, ValidDL rest lcsu)
+  ValidDL dfactors lcsu = ValidDLU dfactors lcsu (UnitOfDimFactors dfactors lcsu)
 
 -- | Are two LCSUs inter-convertible at the given dimension?
 type family ConvertibleLCSUs (dfactors :: [Factor *])
@@ -59,7 +85,7 @@
 type family CanonicalUnitsOfFactors (fs :: [Factor *]) :: [*] where
   CanonicalUnitsOfFactors '[] = '[]
   CanonicalUnitsOfFactors (F u z ': fs) = (CanonicalUnit u) ': CanonicalUnitsOfFactors fs
-    
+
 -- | Check if an LCSU has consistent entries for the given unit. i.e. can the lcsu
 --   describe the unit?
 type family CompatibleUnit (lcsu :: LCSU *) (unit :: *) :: Constraint where
diff --git a/Data/Metrology/Vector.hs b/Data/Metrology/Vector.hs
new file mode 100644
--- /dev/null
+++ b/Data/Metrology/Vector.hs
@@ -0,0 +1,380 @@
+{-# LANGUAGE TypeOperators, FlexibleContexts, DataKinds, TypeFamilies, CPP,
+             ScopedTypeVariables, ConstraintKinds, GeneralizedNewtypeDeriving #-}
+
+#if __GLASGOW_HASKELL__ >= 711
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Metrology.Vector
+-- Copyright   :  (C) 2014 Richard Eisenberg
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Exports combinators for building quantities out of vectors, from the
+-- vector-space library.
+------------------------------------------------------------------------------
+
+module Data.Metrology.Vector (
+  -- * Term-level combinators
+
+  -- | The term-level arithmetic operators are defined by
+  -- applying vertical bar(s) to the sides the dimensioned
+  -- quantities acts on.
+
+  -- ** Additive operations
+  zero, (|+|), (|-|), qSum, qNegate,
+
+  -- ** Multiplicative operations between non-vector quantities
+  (|*|), (|/|), (/|),
+
+  -- ** Multiplicative operations between a vector and a scalar
+  (*|), (|*), (|/),
+
+  -- ** Multiplicative operations on vectors
+  (|*^|), (|^*|), (|^/|), (|.|),
+
+  -- ** Exponentiation
+  (|^), (|^^), qNthRoot,
+  qSq, qCube, qSqrt, qCubeRoot,
+
+  -- ** Other vector operations
+  qMagnitudeSq, qMagnitude, qNormalized, qProject, qCross2, qCross3,
+
+  -- ** Affine operations
+  Point(..), QPoint, (|.-.|), (|.+^|), (|.-^|), qDistanceSq, qDistance,
+  pointNumIn, (.#), quOfPoint, (%.),
+
+  -- ** Comparison
+  qCompare, (|<|), (|>|), (|<=|), (|>=|), (|==|), (|/=|),
+  qApprox, qNapprox,
+
+  -- * Nondimensional units, conversion between quantities and numeric values
+  numIn, (#), quOf, (%), showIn,
+  unity, redim, convert,
+  defaultLCSU, constant,
+
+  -- * Type-level unit combinators
+  (:*)(..), (:/)(..), (:^)(..), (:@)(..),
+  UnitPrefix(..),
+
+  -- * Type-level quantity combinators
+  type (%*), type (%/), type (%^),
+
+  -- * Creating quantity types
+  Qu, MkQu_D, MkQu_DLN, MkQu_U, MkQu_ULN,
+
+  -- * Creating new dimensions
+  Dimension,
+
+  -- * Creating new units
+  Unit(type BaseUnit, type DimOfUnit, conversionRatio),
+  Canonical,
+
+  -- * Numbers, the only built-in unit
+  Dimensionless(..), Number(..), Count, quantity,
+
+  -- * LCSUs (locally coherent system of units)
+  MkLCSU, LCSU(DefaultLCSU), DefaultUnitOfDim,
+
+  -- * Validity checks and assertions
+  CompatibleUnit, CompatibleDim, ConvertibleLCSUs_D,
+  DefaultConvertibleLCSU_D, DefaultConvertibleLCSU_U,
+  MultDimFactors, MultUnitFactors, UnitOfDimFactors,
+
+  -- * Type-level integers
+  Z(..), Succ, Pred, type (#+), type (#-), type (#*), type (#/), Negate,
+
+  -- ** Synonyms for small numbers
+  One, Two, Three, Four, Five, MOne, MTwo, MThree, MFour, MFive,
+
+  -- ** Term-level singletons
+  sZero, sOne, sTwo, sThree, sFour, sFive,
+  sMOne, sMTwo, sMThree, sMFour, sMFive,
+  sSucc, sPred, sNegate,
+
+  -- * Internal definitions
+  -- | The following module is re-exported solely to prevent noise in error messages;
+  -- we do not recommend trying to use these definitions in user code.
+  module Data.Metrology.Internal
+
+  ) where
+
+import Data.Metrology.Qu
+import Data.Metrology.LCSU
+import Data.Metrology.Validity
+import Data.Metrology.Factor
+import Data.Metrology.Z as Z
+import Data.Metrology.Units
+import Data.Metrology.Combinators
+import Data.Metrology.Dimensions
+import Data.Metrology.Internal
+
+import Data.AffineSpace
+import Data.VectorSpace
+import Data.Cross hiding ( One, Two, Three )
+
+import Data.Proxy
+import Data.Coerce
+import Data.Foldable as F
+
+---------------------------------------
+-- Additive operations
+---------------------------------------
+
+-- | The number 0, polymorphic in its dimension. Use of this will
+-- often require a type annotation.
+zero :: AdditiveGroup n => Qu dimspec l n
+zero = Qu zeroV
+
+infixl 6 |+|
+-- | Add two compatible quantities
+(|+|) :: (d1 @~ d2, AdditiveGroup n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
+(Qu a) |+| (Qu b) = Qu (a ^+^ b)
+
+-- | Negate a quantity
+qNegate :: AdditiveGroup n => Qu d l n -> Qu d l n
+qNegate (Qu x) = Qu (negateV x)
+
+infixl 6 |-|
+-- | Subtract two compatible quantities
+(|-|) :: (d1 @~ d2, AdditiveGroup n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n
+a |-| b = a |+| qNegate b
+
+-- | Take the sum of a list of quantities
+qSum :: (Foldable f, AdditiveGroup n) => f (Qu d l n) -> Qu d l n
+qSum = F.foldr (|+|) zero
+
+---------------------------------------
+-- Vector multiplicative operations
+---------------------------------------
+
+infixl 7 |*^|, |^*|, |^/|
+-- | Multiply a scalar quantity by a vector quantity
+(|*^|) :: VectorSpace n => Qu d1 l (Scalar n) -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l n
+(Qu a) |*^| (Qu b) = Qu (a *^ b)
+
+-- | Multiply a vector quantity by a scalar quantity
+(|^*|) :: VectorSpace n => Qu d1 l n -> Qu d2 l (Scalar n) -> Qu (Normalize (d1 @+ d2)) l n
+(Qu a) |^*| (Qu b) = Qu (a ^* b)
+
+-- | Divide a vector quantity by a scalar quantity
+(|^/|) :: (VectorSpace n, Fractional (Scalar n))
+       => Qu d1 l n -> Qu d2 l (Scalar n) -> Qu (Normalize (d1 @- d2)) l n
+(Qu a) |^/| (Qu b) = Qu (a ^/ b)
+
+infixl 7 |/
+-- | Divide a quantity by a scalar
+(|/) :: (VectorSpace n, Fractional (Scalar n)) => Qu a l n -> Scalar n -> Qu a l n
+(Qu a) |/ b = Qu (a ^/ b)
+-- The above function should *not* need to be privileged. But, GHC can't figure
+-- out that a @@- '[] ~ a. Urgh.
+
+infixl 7 *| , |*
+-- | Multiply a quantity by a scalar from the left
+(*|) :: VectorSpace n => Scalar n -> Qu b l n -> Qu b l n
+a *| (Qu b) =  Qu (a *^ b)
+
+-- | Multiply a quantity by a scalar from the right
+(|*) :: VectorSpace n => Qu a l n -> Scalar n -> Qu a l n
+(Qu a) |* b = Qu (a ^* b)
+
+---------------------------------------
+-- Multiplicative operations
+---------------------------------------
+
+infixl 7 |.|
+-- | Take a inner (dot) product between two quantities.
+(|.|) :: InnerSpace n => Qu d1 l n -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l (Scalar n)
+(Qu a) |.| (Qu b) = Qu (a <.> b)
+
+-- | Square the length of a vector.
+qMagnitudeSq :: InnerSpace n => Qu d l n -> Qu (d @* Z.Two) l (Scalar n)
+qMagnitudeSq (Qu x) = Qu (magnitudeSq x)
+
+-- | Length of a vector.
+qMagnitude :: (InnerSpace n, Floating (Scalar n)) => Qu d l n -> Qu d l (Scalar n)
+qMagnitude (Qu x) = Qu (magnitude x)
+
+-- | Vector in same direction as given one but with length of one. If given the zero
+-- vector, then return it. The returned vector is dimensionless.
+qNormalized :: (InnerSpace n, Floating (Scalar n)) => Qu d l n -> Qu '[] l n
+qNormalized (Qu x) = Qu (normalized x)
+
+-- | @qProject u v@ computes the projection of @v@ onto @u@.
+qProject :: (InnerSpace n, Floating (Scalar n)) => Qu d2 l n -> Qu d1 l n -> Qu d1 l n
+qProject (Qu u) (Qu v) = Qu (u `project` v)
+
+-- | Cross product of 2D vectors.
+qCross2 :: HasCross2 n => Qu d l n -> Qu d l n
+qCross2 (Qu x) = Qu (cross2 x)
+
+-- | Cross product of 3D vectors.
+qCross3 :: HasCross3 n => Qu d1 l n -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l n
+qCross3 (Qu x) (Qu y) = Qu (x `cross3` y)
+
+---------------------------------------
+-- Affine space operations
+---------------------------------------
+
+-- | A @Point n@ is an affine space built over @n@. Two @Point@s cannot be added,
+-- but they can be subtracted to yield a difference of type @n@.
+newtype Point n = Point n
+  deriving (Show, Eq, Enum, Bounded)
+
+-- | Make a point quantity from a non-point quantity.
+type family QPoint n where
+  QPoint (Qu d l n) = Qu d l (Point n)
+
+instance AdditiveGroup n => AffineSpace (Point n) where
+  type Diff (Point n) = n
+  (.-.) = coerce ((^-^) :: n -> n -> n)
+  (.+^) = coerce ((^+^) :: n -> n -> n)
+
+-- | Make a point quantity at the given unit. Like 'quOf'.
+quOfPoint :: forall dim lcsu unit n.
+             ( ValidDLU dim lcsu unit
+             , VectorSpace n
+             , Fractional (Scalar n) )
+          => n -> unit -> Qu dim lcsu (Point n)
+quOfPoint n unit = Qu (Point x)
+  where Qu x = quOf n unit :: Qu dim lcsu n
+
+infix 5 %.
+-- | Infix synonym of 'quOfPoint'
+(%.) :: ( ValidDLU dim lcsu unit
+        , VectorSpace n
+        , Fractional (Scalar n) )
+     => n -> unit -> Qu dim lcsu (Point n)
+(%.) = quOfPoint
+
+-- | Extract the numerical value from a point quantity. Like 'numIn'.
+pointNumIn :: forall unit dim lcsu n.
+              ( ValidDLU dim lcsu unit
+              , VectorSpace n
+              , Fractional (Scalar n) )
+           => Qu dim lcsu (Point n) -> unit -> n
+pointNumIn (Qu (Point n)) unit = numIn (Qu n :: Qu dim lcsu n) unit
+
+infix 5 .#
+-- | Infix synonym for 'pointNumIn'.
+(.#) :: (ValidDLU dim lcsu unit, VectorSpace n, Fractional (Scalar n))
+     => Qu dim lcsu (Point n) -> unit -> n
+(.#) = pointNumIn
+
+infixl 6 |.-.|, |.+^|, |.-^|
+
+-- | Subtract point quantities.
+(|.-.|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l (Diff n)
+(Qu a) |.-.| (Qu b) = Qu (a .-. b)
+
+-- | Add a point to a vector.
+(|.+^|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l (Diff n) -> Qu d1 l n
+(Qu a) |.+^| (Qu b) = Qu (a .+^ b)
+
+-- | Subract a vector from a point.
+(|.-^|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l (Diff n) -> Qu d1 l n
+(Qu a) |.-^| (Qu b) = Qu (a .-^ b)
+
+-- | Square of the distance between two points.
+qDistanceSq :: (d1 @~ d2, AffineSpace n, InnerSpace (Diff n))
+            => Qu d1 l n -> Qu d2 l n -> Qu (d1 @* Z.Two) l (Scalar (Diff n))
+qDistanceSq (Qu a) (Qu b) = Qu (a `distanceSq` b)
+
+-- | Distance between two points.
+qDistance :: (d1 @~ d2, AffineSpace n, InnerSpace (Diff n), Floating (Scalar (Diff n)))
+          => Qu d1 l n -> Qu d2 l n -> Qu d1 l (Scalar (Diff n))
+qDistance (Qu a) (Qu b) = Qu (a `distance` b)
+
+---------------------------------------
+-- Top-level operations
+---------------------------------------
+
+-- | Extracts a numerical value from a dimensioned quantity, expressed in
+--   the given unit. For example:
+--
+--   > inMeters :: Length -> Double
+--   > inMeters x = numIn x Meter
+--
+--   or
+--
+--   > inMeters x = x # Meter
+numIn :: forall unit dim lcsu n.
+         ( ValidDLU dim lcsu unit
+         , VectorSpace n
+         , Fractional (Scalar n) )
+      => Qu dim lcsu n -> unit -> n
+numIn (Qu val) u
+  = val ^* fromRational
+             (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))
+              / canonicalConvRatio u)
+
+infix 5 #
+-- | Infix synonym for 'numIn'
+(#) :: ( ValidDLU dim lcsu unit
+       , VectorSpace n
+       , Fractional (Scalar n) )
+    => Qu dim lcsu n -> unit -> n
+(#) = numIn
+
+-- | Creates a dimensioned quantity in the given unit. For example:
+--
+--   > height :: Length
+--   > height = quOf 2.0 Meter
+--
+--   or
+--
+--   > height = 2.0 % Meter
+quOf :: forall unit dim lcsu n.
+         ( ValidDLU dim lcsu unit
+         , VectorSpace n
+         , Fractional (Scalar n) )
+      => n -> unit -> Qu dim lcsu n
+quOf d u
+  = Qu (d ^* fromRational
+               (canonicalConvRatio u
+                / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu))))
+
+infix 5 %
+-- | Infix synonym for 'quOf'
+(%) :: ( ValidDLU dim lcsu unit
+       , VectorSpace n
+       , Fractional (Scalar n) )
+    => n -> unit -> Qu dim lcsu n
+(%) = quOf
+
+-- | Dimension-keeping cast between different CSUs.
+convert :: forall d l1 l2 n.
+  ( ConvertibleLCSUs d l1 l2
+  , VectorSpace n
+  , Fractional (Scalar n) )
+  => Qu d l1 n -> Qu d l2 n
+convert (Qu x) = Qu $ x ^* fromRational (
+  canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l1))
+  / canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l2)))
+
+
+-- | Compute the argument in the @DefaultLCSU@, and present the result as
+-- lcsu-polymorphic dimension-polymorphic value. Named 'constant' because one
+-- of its dominant usecase is to inject constant quantities into
+-- dimension-polymorphic expressions.
+constant :: ( d @~ e
+            , ConvertibleLCSUs e DefaultLCSU l
+            , VectorSpace n
+            , Fractional (Scalar n) )
+         => Qu d DefaultLCSU n -> Qu e l n
+constant = convert . redim
+
+infix 1 `showIn`
+-- | Show a dimensioned quantity in a given unit. (The default @Show@
+-- instance always uses units as specified in the LCSU.)
+showIn :: ( ValidDLU dim lcsu unit
+          , VectorSpace n
+          , Fractional (Scalar n)
+          , Show unit
+          , Show n )
+       => Qu dim lcsu n -> unit -> String
+showIn x u = show (x # u) ++ " " ++ show u
diff --git a/Data/Metrology/Z.hs b/Data/Metrology/Z.hs
--- a/Data/Metrology/Z.hs
+++ b/Data/Metrology/Z.hs
@@ -1,8 +1,8 @@
 {- Data/Metrology/Z.hs
- 
+
    The units Package
    Copyright (c) 2013 Richard Eisenberg
-   eir@cis.upenn.edu
+   rae@cs.brynmawr.edu
 
    This file contains a definition of integers at the type-level, in terms
    of a promoted datatype 'Z'.
@@ -10,7 +10,14 @@
 
 {-# LANGUAGE TypeFamilies, DataKinds, TypeOperators, UndecidableInstances,
              GADTs, PolyKinds, TemplateHaskell, ScopedTypeVariables,
-             EmptyCase #-}
+             EmptyCase, CPP, TypeSynonymInstances, FlexibleInstances,
+             InstanceSigs, FlexibleContexts #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TypeApplications #-}
+#endif
+#if __GLASGOW_HASKELL__ >= 810
+{-# LANGUAGE StandaloneKindSignatures #-}
+#endif
 {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
 
 -----------------------------------------------------------------------------
@@ -18,20 +25,61 @@
 -- Module      :  Data.Metrology.Z
 -- Copyright   :  (C) 2013 Richard Eisenberg
 -- License     :  BSD-style (see LICENSE)
--- Maintainer  :  Richard Eisenberg (eir@cis.upenn.edu)
+-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- This module defines a datatype and operations to represent type-level
 -- integers. Though it's defined as part of the units package, it may be
 -- useful beyond dimensional analysis. If you have a compelling non-units
--- use of this package, please let me (Richard, @eir@ at @cis.upenn.edu@)
+-- use of this package, please let me (Richard, @rae@ at @cs.brynmawr.edu@)
 -- know.
 -----------------------------------------------------------------------------
 
-module Data.Metrology.Z where
+-- allow compilation even without Cabal
+#ifndef MIN_VERSION_singletons
+#define MIN_VERSION_singletons(a,b,c) 1
+#endif
 
+module Data.Metrology.Z (
+  -- * The 'Z' datatype
+  Z(..),
+#if MIN_VERSION_singletons(2,6,0)
+  Sing, SZ(..),
+#else
+  Sing(..), SZ,
+#endif
+
+#if MIN_VERSION_singletons(1,0,0)
+  -- ** Defunctionalization symbols (these can be ignored)
+  ZeroSym0, SSym0, SSym1, PSym0, PSym1,
+#endif
+
+  -- * Conversions
+  zToInt, szToInt,
+
+  -- * Type-level operations
+  -- ** Arithmetic
+  Succ, Pred, Negate, type (#+), type (#-), type (#*), type (#/),
+  sSucc, sPred, sNegate,
+
+  -- ** Comparisons
+  type (Data.Metrology.Z.<), NonNegative,
+
+  -- * Synonyms for certain numbers
+  One, Two, Three, Four, Five, MOne, MTwo, MThree, MFour, MFive,
+  sZero, sOne, sTwo, sThree, sFour, sFive, sMOne, sMTwo, sMThree, sMFour, sMFive,
+
+  -- * Deprecated synonyms
+  pZero, pOne, pTwo, pThree, pFour, pFive, pMOne, pMTwo, pMThree, pMFour, pMFive,
+  pSucc, pPred
+  ) where
+
 import Data.Singletons.TH
+#if MIN_VERSION_singletons(3,0,0)
+import Data.Singletons.Base.TH hiding ( Negate, sNegate, NegateSym0, NegateSym1 )
+#endif
+import GHC.Exts ( Constraint )
 
 -- | The datatype for type-level integers.
 $(singletons [d| data Z = Zero | S Z | P Z deriving Eq |])
@@ -84,15 +132,15 @@
   (P z1) #* z2 = (z1 #* z2) #- z2
 
 -- | Negate an integer
-type family NegZ (z :: Z) :: Z where
-  NegZ Zero = Zero
-  NegZ (S z) = P (NegZ z)
-  NegZ (P z) = S (NegZ z)
+type family Negate (z :: Z) :: Z where
+  Negate Zero = Zero
+  Negate (S z) = P (Negate z)
+  Negate (P z) = S (Negate z)
 
 -- | Divide two integers
 type family (a :: Z) #/ (b :: Z) :: Z where
   Zero #/ b      = Zero
-  a    #/ (P b') = NegZ (a #/ (NegZ (P b')))
+  a    #/ (P b') = Negate (a #/ (Negate (P b')))
   a    #/ b      = ZDiv b b a
 
 -- | Helper function for division
@@ -104,16 +152,23 @@
 
 -- | Less-than comparison
 type family (a :: Z) < (b :: Z) :: Bool where
-  Zero  < Zero   = False
-  Zero  < (S n)  = True
-  Zero  < (P n)  = False
-  (S n) < Zero   = False
-  (S n) < (S n') = n < n'
-  (S n) < (P n') = False
-  (P n) < Zero   = True
-  (P n) < (S n') = True
-  (P n) < (P n') = n < n'
+  -- fully qualify everywhere, because Data.Singletons.TH started exporting <
+  -- at some point
+  Zero  Data.Metrology.Z.< Zero   = False
+  Zero  Data.Metrology.Z.< (S n)  = True
+  Zero  Data.Metrology.Z.< (P n)  = False
+  (S n) Data.Metrology.Z.< Zero   = False
+  (S n) Data.Metrology.Z.< (S n') = n Data.Metrology.Z.< n'
+  (S n) Data.Metrology.Z.< (P n') = False
+  (P n) Data.Metrology.Z.< Zero   = True
+  (P n) Data.Metrology.Z.< (S n') = True
+  (P n) Data.Metrology.Z.< (P n') = n Data.Metrology.Z.< n'
 
+-- | Check if a type-level integer is in fact a natural number
+type family NonNegative z :: Constraint where
+  NonNegative Zero  = ()
+  NonNegative (S z) = ()
+
 type One   = S Zero
 type Two   = S One
 type Three = S Two
@@ -128,31 +183,55 @@
 
 -- | This is the singleton value representing @Zero@ at the term level and
 -- at the type level, simultaneously. Used for raising units to powers.
-pZero  = SZero
-pOne   = SS pZero
-pTwo   = SS pOne
-pThree = SS pTwo
-pFour  = SS pThree
-pFive  = SS pFour
+sZero  = SZero
+sOne   = SS sZero
+sTwo   = SS sOne
+sThree = SS sTwo
+sFour  = SS sThree
+sFive  = SS sFour
 
-pMOne   = SP pZero
-pMTwo   = SP pMOne
-pMThree = SP pMTwo
-pMFour  = SP pMThree
-pMFive  = SP pMFour
+sMOne   = SP sZero
+sMTwo   = SP sMOne
+sMThree = SP sMTwo
+sMFour  = SP sMThree
+sMFive  = SP sMFour
 
 -- | Add one to a singleton @Z@.
-pSucc :: Sing z -> Sing (Succ z)
-pSucc SZero   = pOne
-pSucc (SS z') = SS (SS z')
-pSucc (SP z') = z'
+sSucc :: Sing z -> Sing (Succ z)
+sSucc SZero   = sOne
+sSucc (SS z') = SS (SS z')
+sSucc (SP z') = z'
 
 -- | Subtract one from a singleton @Z@.
-pPred :: Sing z -> Sing (Pred z)
-pPred SZero   = pMOne
-pPred (SS z') = z'
-pPred (SP z') = SP (SP z')
+sPred :: Sing z -> Sing (Pred z)
+sPred SZero   = sMOne
+sPred (SS z') = z'
+sPred (SP z') = SP (SP z')
 
+-- | Negate a singleton @Z@.
+sNegate :: Sing z -> Sing (Negate z)
+sNegate SZero = SZero
+sNegate (SS z') = SP (sNegate z')
+sNegate (SP z') = SS (sNegate z')
+
 -- | Convert a singleton @Z@ to an @Int@.
 szToInt :: Sing (z :: Z) -> Int
 szToInt = zToInt . fromSing
+
+{-# DEPRECATED pZero, pOne, pTwo, pThree, pFour, pFive, pMOne, pMTwo, pMThree, pMFour, pMFive, pSucc, pPred "The singleton prefix is changing from 'p' to 's'. The 'p' versions will be removed in a future release." #-}
+
+pZero  = sZero
+pOne   = sOne
+pTwo   = sTwo
+pThree = sThree
+pFour  = sFour
+pFive  = sFive
+
+pMOne   = sMOne
+pMTwo   = sMTwo
+pMThree = sMThree
+pMFour  = sMFour
+pMFive  = sMFive
+
+pSucc = sSucc
+pPred = sPred
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,16 +3,22 @@
 
 The _units_ package provides a mechanism for compile-time dimensional analysis
 in Haskell programs. It defines an embedded type system based on
-units-of-measure. The units defined are fully extensible, and need not relate
-to physical properties. As a matter of convenience only, the core package
-defines the dimensions and units for the international system (SI), and you
-can find many additional units and dimensions in package _units-extra_.
+units-of-measure. The units and dimensions defined are fully extensible, and
+need not relate to physical properties. This package exports definitions
+only for `Dimensionless` and `Number`. The set of units and dimensions from
+the International System (SI) are exported from the companion package `units-defs`.
 
-The package supports defining multiple inter-convertible units, such
-as `Meter` and `Foot`. When extracting a numerical value from a quantity,
-the desired unit must be specified, and the value is converted into
-that unit.
+This package supports independent notions of _dimension_ and _unit_. Examples
+of dimensions include length and mass. Examples of unit include meter and
+gram. Every unit measures a particular dimension, but a given dimension
+may be measured by many different units. For example, both meters and feet
+measure length.
 
+The package supports defining multiple inter-convertible units of the same
+dimension, such as `Meter` and `Foot`. When extracting a numerical value from
+a quantity, the desired unit must be specified, and the value is converted
+into that unit.
+
 The laws of nature have dimensions, and they hold true regardless of the units
 used. For example, the gravitational force between two bodies is
 `(gravitational constant) * (mass 1) * (mass 2) / (distance between body 1 and
@@ -21,19 +27,22 @@
 
 The _units_ package supports unit-polymorphic programs through the coherent
 system of units (CSU) mechanism. A CSU is essentially a mapping from
-dimensions (such as length or mass) to the units (such as meters or
-kilograms). All dimensioned quantities (generally just called quantities) are
-expressed using the `Qu` type. The `Qu` type constructor takes a (perhaps
-compound) dimension, a CSU and a numerical value type as arguments.
+dimensions to the units. All dimensioned quantities (generally just called
+quantities) are expressed using the `Qu` type. The `Qu` type constructor takes
+a (perhaps compound) dimension, a CSU and a numerical value type as arguments.
 Internally, the quantity is stored as a number in the units as specified in
-the CSU -- this may matter if you are worried about rounding errors.
-In the sequence of computations that works within one CSU,
-there is no unit conversion. Unit conversions are needed only when
-putting values in and out of quantities, or converting between two different
-CSUs.
+the CSU -- this may matter if you are worried about rounding errors. In the
+sequence of computations that works within one CSU, there is no unit
+conversion. Unit conversions are needed only when putting values in and out of
+quantities, or converting between two different CSUs.
 
+Checking out units
+------------------
 
+Units has a git submodule, so you'll want to use `git clone --recursive`. Example:
 
+    git clone --recursive https://github.com/goldfirere/units
+
 User contributions
 ------------------
 
@@ -48,18 +57,41 @@
 Modules
 -------
 
-The _units_ package exports several modules. For any given project, you will
-include some set of these modules. There are dependency relationships
-between them. Of course, you're welcome to `import` a module without its
-dependents, but it probably won't be very useful to you. I hope that this list
-grows over time.
+The _units_ package exports several key modules. Note that you will generally
+import only *one* of `Data.Metrology`, `Data.Metrology.Poly`, or
+`Data.Metrology.Vector`.
 
- -  __`Data.Metrology`__
+ -  __`Data.Metrology.Poly`__
 
     This is the main exported module. It exports all the necessary functionality
-    for you to build your own set of units and operate with them. All modules
-    implicitly depend on this one.
+    for you to build your own set of units and operate with them.
 
+ -  __`Data.Metrology`__
+
+    This re-exports most of the definitions from `Data.Metrology.Poly`, but
+    restricts a few operators to work only with the default LCSU, as this is
+    simpler for new users to `units`.
+
+ -  __`Data.Metrology.Vector`__
+
+    This also re-exports a similar set of definitions as `Data.Metrology.Poly`,
+    but provides numerical operations based on `vector-space` instead of the
+    standard numerical classes.
+
+ -  __`Data.Metrology.Linear`__
+
+    This exports a set of definitions for interoperability with the `linear`
+    package. This is /not/ a top-level import; generally, import this with
+    `Data.Metrology.Poly` as well.
+
+ -  __`Data.Metrology.Internal`__
+
+    This module contains mostly-internal definitions that may appear in GHC's
+    error messages. Users will generally not need to use these definitions in
+    their code. However, by exporting this module from within
+    `Data.Metrology.Poly`, we can reduce the module-prefix clutter in error
+    messages.
+
  -  __`Data.Metrology.Unsafe`__
 
     This module exports the constructor for the central datatype that stores
@@ -71,46 +103,69 @@
     This module defines a `Show` instance for quantities, printing
     out the number stored along with its canonical dimension. This behavior
     may not be the best for every setting, so it is exported separately.
+    Importing this module reduces the guaranteed unit-safety of your code,
+    because it allows you to inspect (in a round-about way) how your quantities
+    are stored.
 
- -  __`Data.Metrology.SI`__
+ -  __`Data.Metrology.Parser`__
 
-    This module exports unit definitions for the [SI][] system of units,
-    re-exporting the three modules below.
+    This module allows users to create custom unit parsers. The user specifies
+    a set of prefixes and a set of units to parse, and then a quasi-quoting parser
+    is generated. See the module documentation for details.
 
-[SI]: http://en.wikipedia.org/wiki/International_System_of_Units
- 
- -  __`Data.Metrology.SI.Units`__
+ -  __`Data.Metrology.TH`__
 
-    This module exports only the SI units, such as `Meter` and `Ampere`.
+    This module exports several functions, written with Template Haskell, that
+    make programming with `units` somewhat easier. In particular, see
+    `declareMonoUnit`, which gets rid of a lot of the boilerplate if you don't
+    want unit polymorphism.
 
- -  __`Data.Metrology.SI.Types`__
+ -  __`Data.Metrology.Quantity`__
 
-    This module exports pre-defined unit type synonyms for SI dimensions,
-    convenient for use with the SI.Units module.
-    For example, `Length` is the type of
-    quantities with unit `Meter`s and with numerical type `Double`.
+    This module defines a `Quantity` class to enable easy, safe conversions with
+    non-`units` types. See the module for more documentation.
 
- -  __`Data.Metrology.SI.Prefixes`__
+Examples
+========
 
-    This module exports the SI prefixes. Note that this does *not* depend on
-    any of the other SI modules -- you can use these prefixes with any system
-    of units.
+We will build up a full working example in several sections. It is awkward to
+explain the details of the pieces until the whole example is built, so please
+read on to see how it all works. For more complete(-ish) examples, see [this
+test
+case](https://github.com/goldfirere/units/blob/master/Tests/Compile/Simulator.hs)
+(for examples of how to use units) and
+[units-defs](https://github.com/goldfirere/units-defs) (for examples of how to
+define units).
 
+Dimension definitions
+---------------------
 
+When setting up your well-typed units-of-measure program, the first step is
+to define the dimensions you will be working in. (If your application involves
+physical quantities, you may want to check `Data.Dimensions.SI` in the
+`units-defs` package first.)
 
-Examples
-========
+    data LengthDim = LengthDim  -- each dimension is a datatype that acts as its own proxy
+    instance Dimension LengthDim
 
-**NOTE: THIS IS OUT OF DATE.**
+    data TimeDim = TimeDim
+    instance Dimension TimeDim
 
+We can now build up dimensions from these base dimensions:
+
+    type VelocityDim = LengthDim :/ TimeDim
+
 Unit definitions
 ----------------
 
-Here is how to define two inter-convertible units:
+We then define units to work with these dimensions. Here, we define two different
+inter-convertible units for length. (Note that just about all of this boilerplate
+can be generated by functions in the `Data.Metrology.TH` module.)
 
-    data Meter = Meter    -- each unit is a datatype that acts as its own proxy
+    data Meter = Meter
     instance Unit Meter where           -- declare Meter as a Unit
       type BaseUnit Meter = Canonical   -- Meters are "canonical"
+      type DimOfUnit Meter = LengthDim  -- Meters measure Lengths
     instance Show Meter where           -- Show instances are optional but useful
       show _ = "m"                      -- do *not* examine the argument!
 
@@ -118,39 +173,115 @@
     instance Unit Foot where
       type BaseUnit Foot = Meter        -- Foot is defined in terms of Meter
       conversionRatio _ = 0.3048        -- do *not* examine the argument!
+                                        -- We don't need to specify the `DimOfUnit`;
+                                        -- it's implied by the `BaseUnit`.
     instance Show Foot where
       show _ = "ft"
 
-    type Length = MkQu Meter           -- we will manipulate Lengths
-    type Length' = MkQu Foot           -- this is the *same* as Length
+    data Second = Second
+    instance Unit Second where
+      type BaseUnit Second = Canonical
+      type DimOfUnit Second = TimeDim
+    instance Show Second where
+      show _ = "s"
 
-    extend :: Length -> Length          -- a function over lengths
-    extend x = dim $ x .+ (1 % Meter)   -- more on this later
+A unit assignment
+-----------------
 
-    inMeters :: Length -> Double        -- extract the # of meters
-    inMeters = (# Meter)                -- more on this later
+To perform computations with _units_, we must define a so-called _local coherent
+set of units_, or LCSU. This is a mapping from dimensions to units, and it informs
+exactly how the quantities are stored. For example:
 
-Let's pick this apart. The `data Meter = Meter` declaration creates both the
-type `Meter` and a term-level proxy for it. It would be possible to get away
-without the proxies and lots of type annotations, but who would want to?
-Then, we define an instance of `Unit` to make `Meter` into a proper unit.
-The `Unit` class is primarily responsible for handling unit conversions.
-In the case of `Meter`, we define that as the _canonical_ unit of length, meaning
-that all lengths will internally be stored in meters. It also means that we
-don't need to define a conversion ratio for meters.
+    type LCSU = MkLCSU '[(LengthDim, Meter), (TimeDim, Second)]
 
+This definition says that we wish to store lengths in meters and times in seconds.
+Note that, even though `Meter` is defined as the `Canonical` length, we could have
+used `Foot` in our LCSU. Canonical units are used only in conversion between
+units, not the choice of how to store a quantity.
+
+Value types
+-----------
+
+To use all these pieces to build the actual type that will store quantities, we
+use one of the `MkQu_xxx` type synonyms, as follows:
+
+    type Length = MkQu_DLN LengthDim LCSU Double
+      -- Length stores lengths in our defined LCSU, using `Double` as the numerical type
+    type Length' = MkQu_ULN Foot LCSU Double
+      -- same as Length. Note the `U` in `MkQu_ULN`, allowing it to take a unit
+
+    type Time = MkQu_DLN TimeDim LCSU Double
+
+Some computations
+-----------------
+
+We now show some example computations on the defined types:
+
+    extend :: Length -> Length            -- a function over lengths
+    extend x = redim $ x |+| (1 % Meter)
+
+    inMeters :: Length -> Double          -- extract the # of meters
+    inMeters = (# Meter)                  -- more on this later
+
+    conversion :: Length                  -- mixing units
+    conversion = (4 % Meter) |+| (10 % Foot)
+
+    vel :: Length %/ Time                 -- The `%*` and `%/` operators allow
+                                          -- you to combine types
+    vel = (3 % Meter) |/| (2 % Second)
+
+Explanation
+-----------
+
+Let's pick this apart. The `data LengthDim = LengthDim` declaration creates both the
+type `LengthDim` and a term-level proxy for it. It would be possible to get away
+without the proxies and use lots of type annotations, but who would want to?
+We must define an instance of `Dimension` to declare that `LengthDim` is a dimension.
+Why suffix with `Dim`? To distinguish the length dimension from the length type.
+Generally, the type is mentioned more often and should be the shorter name.
+
+We then create a `TimeDim` to operate alongside the `LengthDim`. Using the
+`:/` combinator, we can create a `VelocityDim` out of the two dimensions defined
+so far. See below for more information on unit combinators.
+
+Then, we make some units, using similar `data` definitions. We define an
+instance of `Unit` to make `Meter` into a proper unit. The `Unit` class is
+primarily responsible for handling unit conversions. In the case of `Meter`,
+we define that as the _canonical_ unit of length, meaning that all lengths
+will internally be stored in meters. It also means that we don't need to
+define a conversion ratio for meters. You will also see that we say that
+`Meter`s measure the dimension `LengthDim`, through the `DimOfUnit` declaration.
+
 We also include a `Show` instance for `Meter` so that lengths can be printed
 easily. If you don't need to `show` your lengths, there is no need for this
 instance.
 
 When defining `Foot`, we say that its `BaseUnit` is `Meter`, meaning that
-`Foot` is inter-convertible with `Meter`. We also must define the conversion
+`Foot` is inter-convertible with `Meter`. This declaration also says that
+the dimension measured by a `Foot` must be the same as the dimension for
+a `Meter`. We must then define the conversion
 ratio, which is the number of meters in a foot. Note that the
 `conversionRatio` method must take a parameter to fix its type parameter, but
 it _must not_ inspect that parameter. Internally, it will be passed
 `undefined` quite often.
 
-The `MkQu` type synonym makes a quantity for a given unit. Note
+The definition for `Second` is quite similar to that for `Meter`.
+
+The next section of code constructs an "LCSU" -- a local coherent set of units.
+The idea is that we wish to be able to choose a set of units which are to be
+used in the internal, concrete representation. An LCSU is just an association
+list giving a concrete unit for each dimension in your domain. The particular
+LCSU here says that length is stored in meters and time is stored in
+seconds. It would be invalid to specify an LCSU with repeats for either
+dimension or unit.
+
+With all this laid out, we can make the types that store values. _units_
+exports several `MkQu_xxx` type synonyms that vary in the arguments they
+expect. `MkQu_DLN`, for example, takes a dimension, an LCSU, and a
+numerical type. With the definition above, `Length` is now a type suitable
+for storing lengths.
+
+Note
 that `Length` and `Length'` are _the same type_. The `MkQu` machinery notices
 that these two are inter-convertible and will produce the same dimensioned
 quantity.
@@ -159,7 +290,8 @@
 to specify the choice of unit when creating a quantity or
 extracting from a quantity. Thus, other than thinking about the
 vagaries of floating point wibbles and the `Show` instance, it is _completely
-irrelevant_ which unit is canonical. The type `Length` defined here could be
+irrelevant_ which unit the concrete unit in the LCSU.
+The type `Length` defined here could be
 used equally well in a program that deals exclusively in feet as it could in a
 program with meters.
 
@@ -169,6 +301,9 @@
 whether I was at the type level or the term level led me to use the former in
 my work.
 
+Other features
+==============
+
 Prefixes
 --------
 
@@ -200,22 +335,12 @@
 Unit combinators
 ----------------
 
-There are several ways of combining units to create other units. Let's also
-have a unit of time:
-
-    data Second = Second
-    instance Unit Second where
-      type BaseUnit Second = Canonical
-    instance Show Second where
-      show _ = "s"
-
-    type Time = MkQu Second
-
+There are several ways of combining units to create other units.
 Units can be multiplied and divided with the operators `:*` and `:/`, at either
 the term or type level. For example:
 
     type MetersPerSecond = Meter :/ Second
-    type Velocity1 = MkQu MetersPerSecond
+    type Velocity1 = MkQu_ULN MetersPerSecond LCSU Double
 
     speed :: Velocity1
     speed = 20 % (Meter :/ Second)
@@ -232,7 +357,7 @@
 with a `p` (mnemonic: "power"). For example:
 
     type MetersSquared = Meter :^ Two
-    type Area1 = MkQu MetersSquared
+    type Area1 = MkQu_ULN MetersSquared LCSU Double
     type Area2 = Length %^ Two        -- same type as Area1
 
     roomSize :: Area1
@@ -248,7 +373,7 @@
 -------------------
 
 The haddock documentation shows the term-level quantity
-combinators. The only one deserving special mention is `dim`, the
+combinators. The only one deserving special mention is `redim`, the
 dimension-safe cast operator. Expressions written with the _units_ package can
 have their types inferred. This works just fine in practice, but the types are
 terrible, unfortunately. Much better is to use top-level annotations (using
@@ -257,22 +382,51 @@
 function may not exactly match up. This is because quantities have
 a looser notion of type equality than Haskell does. For example, "meter *
 second" should be the same as "second * meter", even though these are in
-different order. The `dim` function checks (at compile time) to make sure its
+different order. The `redim` function checks (at compile time) to make sure its
 input type and output type represent the same underlying dimension and then
 performs a cast from one to the other. This cast is completely free at
 runtime. When providing type annotations, it is good practice to start your
-function with a `dim $` to prevent the possibility of type errors. For
+function with a `redim $` to prevent the possibility of type errors. For
 example, say we redefine velocity a different way:
 
-    type Velocity3 = Scalar %/ Time %* Length
+    type Velocity3 = (MkQu_ULN Number LCSU Double) %/ Time %* Length
     addVels :: Velocity1 -> Velocity1 -> Velocity3
-    addVels v1 v2 = dim $ v1 .+ v2
+    addVels v1 v2 = redim $ v1 |+| v2
 
-This is a bit contrived, but it demonstrates the point. Without the `dim`, the
-`addVels` function would not type-check. Because `dim` needs to know its
+This is a bit contrived, but it demonstrates the point. Without the `redim`, the
+`addVels` function would not type-check. Because `redim` needs to know its
 _result_ type to type-check, it should only be used at the top level, such as
 here, where there is a type annotation to guide it.
 
-Note that `dim` is _always_ dimension-safe -- it will not convert a time to a
+Note that `redim` is _always_ dimension-safe -- it will not convert a time to a
 length!
 
+Monomorphic behavior
+====================
+
+_units_ provides a facility for ignoring LCSUs, if your application does not
+need to worry about numerical precision. The facility is through the type
+family `DefaultUnitOfDim`. For example, with the definitions above, we could
+say
+
+    type instance DefaultUnitOfDim LengthDim = Meter
+    type instance DefaultUnitOfDim TimeDim   = Second
+
+and then use the `DefaultLCSU` for our LCSU. To make the use of the default
+LCSU even easier, the `MkQu_xxx` operators that don't mention an LCSU all
+use the default one. So, we can say
+
+    type Length = MkQu_D LengthDim
+
+and get to work. (This uses `Double` as the underlying numerical representation.)
+
+The module `Data.Metrology.SI` from the _units-defs_ package exports type
+instances for `DefaultUnitOfDim` for the SI types, meaning that you can use
+definitions like this right away.
+
+More examples
+=============
+
+Check out some of the test examples we have written to get more of a feel for
+how this all works,
+[here](https://github.com/goldfirere/units/tree/master/Tests).
diff --git a/Test/Travel.hs b/Test/Travel.hs
deleted file mode 100644
--- a/Test/Travel.hs
+++ /dev/null
@@ -1,54 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE TypeOperators #-}
-
-module Main where
-
-import Data.Metrology
-import Data.Metrology.SI.Poly
-import Data.Metrology.Imperial.Types (Imperial)
-import Data.Metrology.Imperial.Units
-import Data.Metrology.Show
-import qualified Data.Metrology.SI.Dims as D
-
-type PerArea lcsu n = MkQu_DLN (D.Area :^ MOne) lcsu n
-
-fromGLtoED :: MkQu_DLN D.Length Imperial Float
-fromGLtoED = 46.5 % Mile
-
-fuelEfficiency :: PerArea Imperial Float
-fuelEfficiency = 40 % (Mile :/ Gallon)
-
-gasolineDensity :: MkQu_DLN D.Density Imperial Float
-gasolineDensity = 7.29 % (Pound :/ Gallon)
-
-gasolineWeight :: (Fractional f) 
-  => MkQu_DLN D.Length su f -> PerArea su f -> MkQu_DLN D.Density su f -> MkQu_DLN D.Mass su f
-gasolineWeight len0 ef0 den0 = len0 |/| ef0 |*| den0
-
-
-main :: IO ()
-main = do
-  putStrLn $ fromGLtoED `showIn` Mile
-  putStrLn $ fuelEfficiency `showIn` Mile :/ Gallon
-  putStrLn $ gasolineDensity `showIn` Pound :/ Gallon
-  putStrLn $ show $ gasolineWeight fromGLtoED fuelEfficiency gasolineDensity 
-
-  putStrLn ""
-  putStrLn $ fromGLtoED `showIn` (kilo Meter)
-  putStrLn $ fuelEfficiency `showIn`  kilo Meter :/ Liter
-  putStrLn $ gasolineDensity `showIn` kilo Gram :/ Liter
-  putStrLn $ show $ (gasolineWeight 
-    (convert fromGLtoED) (convert fuelEfficiency) (convert gasolineDensity) :: MkQu_DLN D.Mass SI Float)
-
-{---- Execution result ---
-46.5 mi
-39.999996 mi/gal
-7.29 lb/gal
-8.474626 lb
-
-74.834496 km
-14.160248 km/l
-0.7273698 kg/l
-3.8440251 kg
--}
diff --git a/units.cabal b/units.cabal
--- a/units.cabal
+++ b/units.cabal
@@ -1,17 +1,19 @@
 name:           units
-version:        2.0
+version:        2.4.1.5
 cabal-version:  >= 1.10
 synopsis:       A domain-specific type system for dimensional analysis
-homepage:       http://www.cis.upenn.edu/~eir/packages/units
+homepage:       https://github.com/goldfirere/units
 category:       Math
-author:         Richard Eisenberg <eir@cis.upenn.edu>
-maintainer:     Richard Eisenberg <eir@cis.upenn.edu>, Takayuki Muranushi <muranushi@gmail.com>
+author:         Richard Eisenberg <rae@richarde.dev>, Takayuki Muranushi <muranushi@gmail.com>
+maintainer:     Richard Eisenberg <rae@richarde.dev>
 bug-reports:    https://github.com/goldfirere/units/issues
 stability:      experimental
-extra-source-files: README.md, CHANGES.md
+extra-source-files: README.md
+                  , CHANGES.md
 license:        BSD3
 license-file:   LICENSE
 build-type:     Simple
+Tested-With: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.1, GHC == 8.6.3, GHC == 8.8.1, GHC == 8.10.4, GHC == 9.0.1, GHC == 9.2.1
 description:
 
     The units package provides a mechanism for compile-time
@@ -25,63 +27,82 @@
     value is converted into that unit.
 
     If you are looking for specific systems of units (such as SI),
-    please see the `units-defs` package.
+    please see the @units-defs@ package.
 
+    Tests for this package are in a companion package @units-test@,
+    available from this package's source repository.
+
     The Haddock documentation is insufficient for using the units
     package. Please see the README file, available from the package
     home page.
 
+
 source-repository this
   type:     git
   location: https://github.com/goldfirere/units.git
-  tag:      v2.0
+  tag:      v2.4.1.5
 
 library
-  build-depends:      
-      base >= 4.7 && < 5,
-      singletons >= 0.9 && < 1
-  exposed-modules:    
-    Data.Metrology, 
+  ghc-options: -Wall
+
+  if impl(ghc >= 7.10)
+    ghc-options: -fno-warn-unticked-promoted-constructors
+
+  if impl(ghc >= 9.0)
+    build-depends: base >= 4.7 && < 5
+                 , th-desugar >= 1.5.4
+                 , singletons == 3.*
+                 , singletons-th == 3.*
+                 , singletons-base == 3.*
+                 , vector-space >= 0.8
+                 , linear >= 1.16.2
+                 , template-haskell
+                 , mtl >= 1.1
+                 , multimap >= 1.2
+                 , syb >= 0.3
+                 , containers >= 0.4
+                 , units-parser >= 0.1 && < 1.0
+                 , lens >= 4 && < 6
+                 , deepseq >= 1.1.0.0 && < 1.5
+  else
+    build-depends: base >= 4.7 && < 5
+                 , th-desugar >= 1.5.4
+                 , singletons >= 0.9 && < 3
+                       -- keep it < 3 to avoid the need for singletons-th and -base
+                 , vector-space >= 0.8
+                 , linear >= 1.16.2
+                 , template-haskell
+                 , mtl >= 1.1
+                 , multimap >= 1.2
+                 , syb >= 0.3
+                 , containers >= 0.4
+                 , units-parser >= 0.1 && < 1.0
+                 , lens >= 4 && < 6
+                 , deepseq >= 1.1.0.0 && < 1.5
+  exposed-modules:
+    Data.Metrology,
     Data.Metrology.Internal,
     Data.Metrology.Show,
     Data.Metrology.Unsafe,
     Data.Metrology.Z,
-    Data.Metrology.Set
+    Data.Metrology.Set,
+    Data.Metrology.Vector,
+    Data.Metrology.Linear,
+    Data.Metrology.Parser,
+    Data.Metrology.Poly,
+    Data.Metrology.TH,
+    Data.Metrology.Quantity
 
-  other-modules:     
+  other-modules:
     Data.Metrology.Factor,
     Data.Metrology.LCSU,
-    Data.Metrology.Quantity, 
+    Data.Metrology.Qu,
     Data.Metrology.Dimensions,
     Data.Metrology.Units,
     Data.Metrology.Combinators,
     Data.Metrology.Validity
 
     -- cabal now recommends that TH be explicitly listed in cabal files
-  default-extensions: TemplateHaskell
-  default-language:   Haskell2010
-
-
--- Here are some test scripts for `units`. Since `units` is a
--- type-level library and mainly works at compile time, we're just
--- testing if these programs compiles and runs properlly.
--- 
--- !IMPORTANT! You need to type
---
--- > make prepare-test
---
--- once, to bring the specific unit definitions from `units-defs`
--- into scope to run the tests.
-
-Test-Suite travel
-  Type:               exitcode-stdio-1.0
-  Hs-Source-Dirs:     Test
-  Ghc-Options:        -Wall
-  Main-Is:            Travel.hs
-  Other-Modules:        
-                        
-  Build-Depends:        
-      base
-    , units
+  other-extensions: TemplateHaskell
+  default-language: Haskell2010
 
-  default-language:   Haskell2010
