packages feed

units 2.0 → 2.4.1.5

raw patch · 23 files changed

Files

CHANGES.md view
@@ -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 ----------- 
Data/Metrology.hs view
@@ -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
Data/Metrology/Combinators.hs view
@@ -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)
Data/Metrology/Dimensions.hs view
@@ -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]-  
Data/Metrology/Factor.hs view
@@ -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
Data/Metrology/Internal.hs view
@@ -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--  
Data/Metrology/LCSU.hs view
@@ -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
+ Data/Metrology/Linear.hs view
@@ -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
+ Data/Metrology/Parser.hs view
@@ -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
+ Data/Metrology/Poly.hs view
@@ -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)
+ Data/Metrology/Qu.hs view
@@ -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
Data/Metrology/Quantity.hs view
@@ -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
Data/Metrology/Set.hs view
@@ -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 --
Data/Metrology/Show.hs view
@@ -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)))
+ Data/Metrology/TH.hs view
@@ -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 []
Data/Metrology/Units.hs view
@@ -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 = '[]
Data/Metrology/Unsafe.hs view
@@ -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
Data/Metrology/Validity.hs view
@@ -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
+ Data/Metrology/Vector.hs view
@@ -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
Data/Metrology/Z.hs view
@@ -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
README.md view
@@ -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).
− Test/Travel.hs
@@ -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--}
units.cabal view
@@ -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