dimensional 1.1 → 1.2
raw patch · 30 files changed
+5199/−5181 lines, 30 filesdep ~basesetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Numeric.Units.Dimensional: }
- Numeric.Units.Dimensional.Dimensions.TermLevel: instance Data.Semigroup.Semigroup Numeric.Units.Dimensional.Dimensions.TermLevel.Dimension'
- Numeric.Units.Dimensional.Dynamic: instance Data.Semigroup.Semigroup Numeric.Units.Dimensional.Dynamic.AnyUnit
- Numeric.Units.Dimensional.Dynamic: instance GHC.Num.Num a => Data.Semigroup.Semigroup (Numeric.Units.Dimensional.Dynamic.AnyQuantity a)
- Numeric.Units.Dimensional.Dynamic: instance GHC.Num.Num a => Data.Semigroup.Semigroup (Numeric.Units.Dimensional.Dynamic.DynQuantity a)
- Numeric.Units.Dimensional.FixedPoint: }
+ Numeric.Units.Dimensional: type family NRoot (d :: Dimension) (x :: TypeInt)
+ Numeric.Units.Dimensional.Coercion: data family Dimensional v :: Dimension -> Type -> Type
+ Numeric.Units.Dimensional.Dimensions: AnyDimension :: DynamicDimension
+ Numeric.Units.Dimensional.Dimensions: Dim' :: !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> Dimension'
+ Numeric.Units.Dimensional.Dimensions: NoDimension :: DynamicDimension
+ Numeric.Units.Dimensional.Dimensions: SomeDimension :: Dimension' -> DynamicDimension
+ Numeric.Units.Dimensional.Dimensions: asList :: Dimension' -> [Int]
+ Numeric.Units.Dimensional.Dimensions: class HasDynamicDimension a => HasDimension a
+ Numeric.Units.Dimensional.Dimensions: class HasDynamicDimension a
+ Numeric.Units.Dimensional.Dimensions: dOne :: Dimension'
+ Numeric.Units.Dimensional.Dimensions: data Dimension'
+ Numeric.Units.Dimensional.Dimensions: data DynamicDimension
+ Numeric.Units.Dimensional.Dimensions: dimension :: HasDimension a => a -> Dimension'
+ Numeric.Units.Dimensional.Dimensions: dynamicDimension :: (HasDynamicDimension a, HasDimension a) => a -> DynamicDimension
+ Numeric.Units.Dimensional.Dimensions: hasSomeDimension :: HasDynamicDimension a => a -> Bool
+ Numeric.Units.Dimensional.Dimensions: isCompatibleWith :: HasDynamicDimension a => a -> Dimension' -> Bool
+ Numeric.Units.Dimensional.Dimensions: matchDimensions :: DynamicDimension -> DynamicDimension -> DynamicDimension
+ Numeric.Units.Dimensional.Dimensions.TermLevel: instance GHC.Base.Semigroup Numeric.Units.Dimensional.Dimensions.TermLevel.Dimension'
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: infixl 7 /
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: infixr 8 ^
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type family NRoot (d :: Dimension) (x :: TypeInt)
+ Numeric.Units.Dimensional.Dynamic: instance GHC.Base.Semigroup Numeric.Units.Dimensional.Dynamic.AnyUnit
+ Numeric.Units.Dimensional.Dynamic: instance GHC.Num.Num a => GHC.Base.Semigroup (Numeric.Units.Dimensional.Dynamic.AnyQuantity a)
+ Numeric.Units.Dimensional.Dynamic: instance GHC.Num.Num a => GHC.Base.Semigroup (Numeric.Units.Dimensional.Dynamic.DynQuantity a)
+ Numeric.Units.Dimensional.FixedPoint: infixr 8 ^
+ Numeric.Units.Dimensional.FixedPoint: type family NRoot (d :: Dimension) (x :: TypeInt)
+ Numeric.Units.Dimensional.Prelude: (!!) :: () => [a] -> Int -> a
+ Numeric.Units.Dimensional.Prelude: ($!) :: () => (a -> b) -> a -> b
+ Numeric.Units.Dimensional.Prelude: ($) :: () => (a -> b) -> a -> b
+ Numeric.Units.Dimensional.Prelude: (&&) :: Bool -> Bool -> Bool
+ Numeric.Units.Dimensional.Prelude: (*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 * v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 * v2) (d1 * d2) a
+ Numeric.Units.Dimensional.Prelude: (**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: (*>) :: Applicative f => f a -> f b -> f b
+ Numeric.Units.Dimensional.Prelude: (*~) :: Num a => a -> Unit m d a -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: (*~~) :: (Functor f, Num a) => f a -> Unit m d a -> f (Quantity d a)
+ Numeric.Units.Dimensional.Prelude: (+) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: (++) :: () => [a] -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: (-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: (.) :: Category cat => cat b c -> cat a b -> cat a c
+ Numeric.Units.Dimensional.Prelude: (/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 / v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 / v2) (d1 / d2) a
+ Numeric.Units.Dimensional.Prelude: (/=) :: Eq a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (/~) :: Fractional a => Quantity d a -> Unit m d a -> a
+ Numeric.Units.Dimensional.Prelude: (/~~) :: forall f m d a. (Functor f, Fractional a) => f (Quantity d a) -> Unit m d a -> f a
+ Numeric.Units.Dimensional.Prelude: (<$) :: Functor f => a -> f b -> f a
+ Numeric.Units.Dimensional.Prelude: (<$>) :: Functor f => (a -> b) -> f a -> f b
+ Numeric.Units.Dimensional.Prelude: (<) :: Ord a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (<*) :: Applicative f => f a -> f b -> f a
+ Numeric.Units.Dimensional.Prelude: (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+ Numeric.Units.Dimensional.Prelude: (<=) :: Ord a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (<>) :: Semigroup a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: (=<<) :: Monad m => (a -> m b) -> m a -> m b
+ Numeric.Units.Dimensional.Prelude: (==) :: Eq a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (>) :: Ord a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (>=) :: Ord a => a -> a -> Bool
+ Numeric.Units.Dimensional.Prelude: (>>) :: Monad m => m a -> m b -> m b
+ Numeric.Units.Dimensional.Prelude: (>>=) :: Monad m => m a -> (a -> m b) -> m b
+ Numeric.Units.Dimensional.Prelude: (^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v)) => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a
+ Numeric.Units.Dimensional.Prelude: (^/) :: (KnownTypeInt n, Floating a) => Quantity d a -> Proxy n -> Quantity (NRoot d n) a
+ Numeric.Units.Dimensional.Prelude: (^^) :: (Fractional a, Integral b) => a -> b -> a
+ Numeric.Units.Dimensional.Prelude: (||) :: Bool -> Bool -> Bool
+ Numeric.Units.Dimensional.Prelude: -- parameterized by its <a>Dimension</a> and representation.
+ Numeric.Units.Dimensional.Prelude: -- | A dimensional value, either a <a>Quantity</a> or a <a>Unit</a>,
+ Numeric.Units.Dimensional.Prelude: Dim :: TypeInt -> TypeInt -> TypeInt -> TypeInt -> TypeInt -> TypeInt -> TypeInt -> Dimension
+ Numeric.Units.Dimensional.Prelude: Dim' :: !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> Dimension'
+ Numeric.Units.Dimensional.Prelude: EQ :: Ordering
+ Numeric.Units.Dimensional.Prelude: False :: Bool
+ Numeric.Units.Dimensional.Prelude: GT :: Ordering
+ Numeric.Units.Dimensional.Prelude: Just :: a -> Maybe a
+ Numeric.Units.Dimensional.Prelude: LT :: Ordering
+ Numeric.Units.Dimensional.Prelude: Left :: a -> Either a b
+ Numeric.Units.Dimensional.Prelude: Metric :: Metricality
+ Numeric.Units.Dimensional.Prelude: NonMetric :: Metricality
+ Numeric.Units.Dimensional.Prelude: Nothing :: Maybe a
+ Numeric.Units.Dimensional.Prelude: Right :: b -> Either a b
+ Numeric.Units.Dimensional.Prelude: True :: Bool
+ Numeric.Units.Dimensional.Prelude: _0 :: Num a => Quantity d a
+ Numeric.Units.Dimensional.Prelude: _1 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _2 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _3 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _4 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _5 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _6 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _7 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _8 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: _9 :: Num a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: abs :: Num a => Quantity d a -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: acos :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: acosh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: all :: Foldable t => (a -> Bool) -> t a -> Bool
+ Numeric.Units.Dimensional.Prelude: and :: Foldable t => t Bool -> Bool
+ Numeric.Units.Dimensional.Prelude: any :: Foldable t => (a -> Bool) -> t a -> Bool
+ Numeric.Units.Dimensional.Prelude: appendFile :: FilePath -> String -> IO ()
+ Numeric.Units.Dimensional.Prelude: asLens :: Fractional a => Unit m d a -> forall f. Functor f => (a -> f a) -> Quantity d a -> f (Quantity d a)
+ Numeric.Units.Dimensional.Prelude: asTypeOf :: () => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: asin :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: asinh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: atan :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: atan2 :: RealFloat a => Quantity d a -> Quantity d a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: atanh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: break :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Numeric.Units.Dimensional.Prelude: cbrt :: Floating a => Quantity d a -> Quantity (Cbrt d) a
+ Numeric.Units.Dimensional.Prelude: ceiling :: (RealFrac a, Integral b) => a -> b
+ Numeric.Units.Dimensional.Prelude: changeRep :: (KnownVariant v, Real a, Fractional b) => Dimensional v d a -> Dimensional v d b
+ Numeric.Units.Dimensional.Prelude: changeRepApproximate :: (KnownVariant v, Floating b) => Dimensional v d ExactPi -> Dimensional v d b
+ Numeric.Units.Dimensional.Prelude: class Functor f => Applicative (f :: Type -> Type)
+ Numeric.Units.Dimensional.Prelude: class Bounded a
+ Numeric.Units.Dimensional.Prelude: class Category (cat :: k -> k -> Type)
+ Numeric.Units.Dimensional.Prelude: class Enum a
+ Numeric.Units.Dimensional.Prelude: class Eq a
+ Numeric.Units.Dimensional.Prelude: class Fractional a => Floating a
+ Numeric.Units.Dimensional.Prelude: class Foldable (t :: Type -> Type)
+ Numeric.Units.Dimensional.Prelude: class Num a => Fractional a
+ Numeric.Units.Dimensional.Prelude: class Functor (f :: Type -> Type)
+ Numeric.Units.Dimensional.Prelude: class HasDynamicDimension a => HasDimension a
+ Numeric.Units.Dimensional.Prelude: class (Real a, Enum a) => Integral a
+ Numeric.Units.Dimensional.Prelude: class KnownVariant (v :: Variant) where {
+ Numeric.Units.Dimensional.Prelude: class Applicative m => Monad (m :: Type -> Type)
+ Numeric.Units.Dimensional.Prelude: class Semigroup a => Monoid a
+ Numeric.Units.Dimensional.Prelude: class Num a
+ Numeric.Units.Dimensional.Prelude: class Eq a => Ord a
+ Numeric.Units.Dimensional.Prelude: class Read a
+ Numeric.Units.Dimensional.Prelude: class (Num a, Ord a) => Real a
+ Numeric.Units.Dimensional.Prelude: class (RealFrac a, Floating a) => RealFloat a
+ Numeric.Units.Dimensional.Prelude: class (Real a, Fractional a) => RealFrac a
+ Numeric.Units.Dimensional.Prelude: class Semigroup a
+ Numeric.Units.Dimensional.Prelude: class Show a
+ Numeric.Units.Dimensional.Prelude: class (Functor t, Foldable t) => Traversable (t :: Type -> Type)
+ Numeric.Units.Dimensional.Prelude: compare :: Ord a => a -> a -> Ordering
+ Numeric.Units.Dimensional.Prelude: concat :: Foldable t => t [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
+ Numeric.Units.Dimensional.Prelude: const :: () => a -> b -> a
+ Numeric.Units.Dimensional.Prelude: cos :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: cosh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: curry :: () => ((a, b) -> c) -> a -> b -> c
+ Numeric.Units.Dimensional.Prelude: cycle :: () => [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: data Bool
+ Numeric.Units.Dimensional.Prelude: data Char
+ Numeric.Units.Dimensional.Prelude: data Dimension
+ Numeric.Units.Dimensional.Prelude: data Dimension'
+ Numeric.Units.Dimensional.Prelude: data Double
+ Numeric.Units.Dimensional.Prelude: data Either a b
+ Numeric.Units.Dimensional.Prelude: data Float
+ Numeric.Units.Dimensional.Prelude: data IO a
+ Numeric.Units.Dimensional.Prelude: data Int
+ Numeric.Units.Dimensional.Prelude: data Integer
+ Numeric.Units.Dimensional.Prelude: data Maybe a
+ Numeric.Units.Dimensional.Prelude: data Metricality
+ Numeric.Units.Dimensional.Prelude: data Ordering
+ Numeric.Units.Dimensional.Prelude: data Word
+ Numeric.Units.Dimensional.Prelude: data family Dimensional v :: Dimension -> Type -> Type;
+ Numeric.Units.Dimensional.Prelude: decodeFloat :: RealFloat a => a -> (Integer, Int)
+ Numeric.Units.Dimensional.Prelude: dimension :: HasDimension a => a -> Dimension'
+ Numeric.Units.Dimensional.Prelude: dimensionlessLength :: (Num a, Foldable f) => f b -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: div :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: divMod :: Integral a => a -> a -> (a, a)
+ Numeric.Units.Dimensional.Prelude: drop :: () => Int -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: dropWhile :: () => (a -> Bool) -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: either :: () => (a -> c) -> (b -> c) -> Either a b -> c
+ Numeric.Units.Dimensional.Prelude: elem :: (Foldable t, Eq a) => a -> t a -> Bool
+ Numeric.Units.Dimensional.Prelude: encodeFloat :: RealFloat a => Integer -> Int -> a
+ Numeric.Units.Dimensional.Prelude: enumFrom :: Enum a => a -> [a]
+ Numeric.Units.Dimensional.Prelude: enumFromThen :: Enum a => a -> a -> [a]
+ Numeric.Units.Dimensional.Prelude: enumFromThenTo :: Enum a => a -> a -> a -> [a]
+ Numeric.Units.Dimensional.Prelude: enumFromTo :: Enum a => a -> a -> [a]
+ Numeric.Units.Dimensional.Prelude: error :: HasCallStack => [Char] -> a
+ Numeric.Units.Dimensional.Prelude: errorWithoutStackTrace :: () => [Char] -> a
+ Numeric.Units.Dimensional.Prelude: even :: Integral a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: exactValue :: Unit m d a -> ExactPi
+ Numeric.Units.Dimensional.Prelude: exactify :: Unit m d a -> Unit m d ExactPi
+ Numeric.Units.Dimensional.Prelude: exp :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: exponent :: RealFloat a => a -> Int
+ Numeric.Units.Dimensional.Prelude: fail :: Monad m => String -> m a
+ Numeric.Units.Dimensional.Prelude: filter :: () => (a -> Bool) -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: flip :: () => (a -> b -> c) -> b -> a -> c
+ Numeric.Units.Dimensional.Prelude: floatDigits :: RealFloat a => a -> Int
+ Numeric.Units.Dimensional.Prelude: floatRadix :: RealFloat a => a -> Integer
+ Numeric.Units.Dimensional.Prelude: floatRange :: RealFloat a => a -> (Int, Int)
+ Numeric.Units.Dimensional.Prelude: floor :: (RealFrac a, Integral b) => a -> b
+ Numeric.Units.Dimensional.Prelude: fmap :: Functor f => (a -> b) -> f a -> f b
+ Numeric.Units.Dimensional.Prelude: foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
+ Numeric.Units.Dimensional.Prelude: foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
+ Numeric.Units.Dimensional.Prelude: foldl1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Numeric.Units.Dimensional.Prelude: foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
+ Numeric.Units.Dimensional.Prelude: foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Numeric.Units.Dimensional.Prelude: fromEnum :: Enum a => a -> Int
+ Numeric.Units.Dimensional.Prelude: fromInteger :: Num a => Integer -> a
+ Numeric.Units.Dimensional.Prelude: fromIntegral :: (Integral a, Num b) => a -> b
+ Numeric.Units.Dimensional.Prelude: fromRational :: Fractional a => Rational -> a
+ Numeric.Units.Dimensional.Prelude: fst :: () => (a, b) -> a
+ Numeric.Units.Dimensional.Prelude: gcd :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: getChar :: IO Char
+ Numeric.Units.Dimensional.Prelude: getContents :: IO String
+ Numeric.Units.Dimensional.Prelude: getLine :: IO String
+ Numeric.Units.Dimensional.Prelude: head :: () => [a] -> a
+ Numeric.Units.Dimensional.Prelude: id :: Category cat => cat a a
+ Numeric.Units.Dimensional.Prelude: infix 4 `notElem`
+ Numeric.Units.Dimensional.Prelude: infixl 1 >>
+ Numeric.Units.Dimensional.Prelude: infixl 4 <$>
+ Numeric.Units.Dimensional.Prelude: infixl 6 -
+ Numeric.Units.Dimensional.Prelude: infixl 7 `mod`
+ Numeric.Units.Dimensional.Prelude: infixl 9 !!
+ Numeric.Units.Dimensional.Prelude: infixr 0 $!
+ Numeric.Units.Dimensional.Prelude: infixr 1 =<<
+ Numeric.Units.Dimensional.Prelude: infixr 2 ||
+ Numeric.Units.Dimensional.Prelude: infixr 3 &&
+ Numeric.Units.Dimensional.Prelude: infixr 5 ++
+ Numeric.Units.Dimensional.Prelude: infixr 6 <>
+ Numeric.Units.Dimensional.Prelude: infixr 8 ^^
+ Numeric.Units.Dimensional.Prelude: infixr 9 .
+ Numeric.Units.Dimensional.Prelude: init :: () => [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: interact :: (String -> String) -> IO ()
+ Numeric.Units.Dimensional.Prelude: ioError :: () => IOError -> IO a
+ Numeric.Units.Dimensional.Prelude: isDenormalized :: RealFloat a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: isIEEE :: RealFloat a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: isInfinite :: RealFloat a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: isNaN :: RealFloat a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: isNegativeZero :: RealFloat a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: iterate :: () => (a -> a) -> a -> [a]
+ Numeric.Units.Dimensional.Prelude: last :: () => [a] -> a
+ Numeric.Units.Dimensional.Prelude: lcm :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: length :: Foldable t => t a -> Int
+ Numeric.Units.Dimensional.Prelude: lex :: ReadS String
+ Numeric.Units.Dimensional.Prelude: lines :: String -> [String]
+ Numeric.Units.Dimensional.Prelude: log :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: logBase :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: lookup :: Eq a => a -> [(a, b)] -> Maybe b
+ Numeric.Units.Dimensional.Prelude: map :: () => (a -> b) -> [a] -> [b]
+ Numeric.Units.Dimensional.Prelude: mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
+ Numeric.Units.Dimensional.Prelude: mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
+ Numeric.Units.Dimensional.Prelude: mappend :: Monoid a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: max :: Ord a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: maxBound :: Bounded a => a
+ Numeric.Units.Dimensional.Prelude: maximum :: (Foldable t, Ord a) => t a -> a
+ Numeric.Units.Dimensional.Prelude: maybe :: () => b -> (a -> b) -> Maybe a -> b
+ Numeric.Units.Dimensional.Prelude: mconcat :: Monoid a => [a] -> a
+ Numeric.Units.Dimensional.Prelude: mean :: (Fractional a, Foldable f) => f (Quantity d a) -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: mempty :: Monoid a => a
+ Numeric.Units.Dimensional.Prelude: min :: Ord a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: minBound :: Bounded a => a
+ Numeric.Units.Dimensional.Prelude: minimum :: (Foldable t, Ord a) => t a -> a
+ Numeric.Units.Dimensional.Prelude: mkUnitQ :: Fractional a => UnitName m -> Rational -> Unit m1 d a -> Unit m d a
+ Numeric.Units.Dimensional.Prelude: mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a
+ Numeric.Units.Dimensional.Prelude: mkUnitZ :: Num a => UnitName m -> Integer -> Unit m1 d a -> Unit m d a
+ Numeric.Units.Dimensional.Prelude: mod :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: nFromTo :: (Fractional a, Integral b) => Quantity d a -> Quantity d a -> b -> [Quantity d a]
+ Numeric.Units.Dimensional.Prelude: name :: Unit m d a -> UnitName m
+ Numeric.Units.Dimensional.Prelude: neg1 :: Proxy Neg1
+ Numeric.Units.Dimensional.Prelude: neg2 :: Proxy Neg2
+ Numeric.Units.Dimensional.Prelude: neg3 :: Proxy Neg3
+ Numeric.Units.Dimensional.Prelude: neg4 :: Proxy Neg4
+ Numeric.Units.Dimensional.Prelude: neg5 :: Proxy Neg5
+ Numeric.Units.Dimensional.Prelude: negate :: Num a => Quantity d a -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: not :: Bool -> Bool
+ Numeric.Units.Dimensional.Prelude: notElem :: (Foldable t, Eq a) => a -> t a -> Bool
+ Numeric.Units.Dimensional.Prelude: nroot :: (KnownTypeInt n, Floating a) => Proxy n -> Quantity d a -> Quantity (NRoot d n) a
+ Numeric.Units.Dimensional.Prelude: null :: Foldable t => t a -> Bool
+ Numeric.Units.Dimensional.Prelude: odd :: Integral a => a -> Bool
+ Numeric.Units.Dimensional.Prelude: one :: Num a => Unit 'NonMetric DOne a
+ Numeric.Units.Dimensional.Prelude: or :: Foldable t => t Bool -> Bool
+ Numeric.Units.Dimensional.Prelude: otherwise :: Bool
+ Numeric.Units.Dimensional.Prelude: pi :: Floating a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: pos1 :: Proxy Pos1
+ Numeric.Units.Dimensional.Prelude: pos2 :: Proxy Pos2
+ Numeric.Units.Dimensional.Prelude: pos3 :: Proxy Pos3
+ Numeric.Units.Dimensional.Prelude: pos4 :: Proxy Pos4
+ Numeric.Units.Dimensional.Prelude: pos5 :: Proxy Pos5
+ Numeric.Units.Dimensional.Prelude: pred :: Enum a => a -> a
+ Numeric.Units.Dimensional.Prelude: print :: Show a => a -> IO ()
+ Numeric.Units.Dimensional.Prelude: product :: (Num a, Foldable f) => f (Dimensionless a) -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: properFraction :: (RealFrac a, Integral b) => a -> (b, a)
+ Numeric.Units.Dimensional.Prelude: pure :: Applicative f => a -> f a
+ Numeric.Units.Dimensional.Prelude: putChar :: Char -> IO ()
+ Numeric.Units.Dimensional.Prelude: putStr :: String -> IO ()
+ Numeric.Units.Dimensional.Prelude: putStrLn :: String -> IO ()
+ Numeric.Units.Dimensional.Prelude: quot :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: quotRem :: Integral a => a -> a -> (a, a)
+ Numeric.Units.Dimensional.Prelude: read :: Read a => String -> a
+ Numeric.Units.Dimensional.Prelude: readFile :: FilePath -> IO String
+ Numeric.Units.Dimensional.Prelude: readIO :: Read a => String -> IO a
+ Numeric.Units.Dimensional.Prelude: readList :: Read a => ReadS [a]
+ Numeric.Units.Dimensional.Prelude: readLn :: Read a => IO a
+ Numeric.Units.Dimensional.Prelude: readParen :: () => Bool -> ReadS a -> ReadS a
+ Numeric.Units.Dimensional.Prelude: reads :: Read a => ReadS a
+ Numeric.Units.Dimensional.Prelude: readsPrec :: Read a => Int -> ReadS a
+ Numeric.Units.Dimensional.Prelude: realToFrac :: (Real a, Fractional b) => a -> b
+ Numeric.Units.Dimensional.Prelude: recip :: Fractional a => Quantity d a -> Quantity (Recip d) a
+ Numeric.Units.Dimensional.Prelude: rem :: Integral a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: repeat :: () => a -> [a]
+ Numeric.Units.Dimensional.Prelude: replicate :: () => Int -> a -> [a]
+ Numeric.Units.Dimensional.Prelude: return :: Monad m => a -> m a
+ Numeric.Units.Dimensional.Prelude: reverse :: () => [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: round :: (RealFrac a, Integral b) => a -> b
+ Numeric.Units.Dimensional.Prelude: scaleFloat :: RealFloat a => Int -> a -> a
+ Numeric.Units.Dimensional.Prelude: scanl :: () => (b -> a -> b) -> b -> [a] -> [b]
+ Numeric.Units.Dimensional.Prelude: scanl1 :: () => (a -> a -> a) -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: scanr :: () => (a -> b -> b) -> b -> [a] -> [b]
+ Numeric.Units.Dimensional.Prelude: scanr1 :: () => (a -> a -> a) -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: seq :: () => a -> b -> b
+ Numeric.Units.Dimensional.Prelude: sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
+ Numeric.Units.Dimensional.Prelude: sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
+ Numeric.Units.Dimensional.Prelude: sequence_ :: (Foldable t, Monad m) => t (m a) -> m ()
+ Numeric.Units.Dimensional.Prelude: show :: Show a => a -> String
+ Numeric.Units.Dimensional.Prelude: showChar :: Char -> ShowS
+ Numeric.Units.Dimensional.Prelude: showIn :: (Show a, Fractional a) => Unit m d a -> Quantity d a -> String
+ Numeric.Units.Dimensional.Prelude: showList :: Show a => [a] -> ShowS
+ Numeric.Units.Dimensional.Prelude: showParen :: Bool -> ShowS -> ShowS
+ Numeric.Units.Dimensional.Prelude: showString :: String -> ShowS
+ Numeric.Units.Dimensional.Prelude: shows :: Show a => a -> ShowS
+ Numeric.Units.Dimensional.Prelude: showsPrec :: Show a => Int -> a -> ShowS
+ Numeric.Units.Dimensional.Prelude: siUnit :: forall d a. (KnownDimension d, Num a) => Unit 'NonMetric d a
+ Numeric.Units.Dimensional.Prelude: significand :: RealFloat a => a -> a
+ Numeric.Units.Dimensional.Prelude: signum :: Num a => Quantity d a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: sin :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: sinh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: snd :: () => (a, b) -> b
+ Numeric.Units.Dimensional.Prelude: span :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Numeric.Units.Dimensional.Prelude: splitAt :: () => Int -> [a] -> ([a], [a])
+ Numeric.Units.Dimensional.Prelude: sqrt :: Floating a => Quantity d a -> Quantity (Sqrt d) a
+ Numeric.Units.Dimensional.Prelude: strengthen :: Unit m d a -> Maybe (Unit 'Metric d a)
+ Numeric.Units.Dimensional.Prelude: subtract :: Num a => a -> a -> a
+ Numeric.Units.Dimensional.Prelude: succ :: Enum a => a -> a
+ Numeric.Units.Dimensional.Prelude: sum :: (Num a, Foldable f) => f (Quantity d a) -> Quantity d a
+ Numeric.Units.Dimensional.Prelude: tail :: () => [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: take :: () => Int -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: takeWhile :: () => (a -> Bool) -> [a] -> [a]
+ Numeric.Units.Dimensional.Prelude: tan :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: tanh :: Floating a => Dimensionless a -> Dimensionless a
+ Numeric.Units.Dimensional.Prelude: tau :: Floating a => Dimensionless a
+ Numeric.Units.Dimensional.Prelude: toEnum :: Enum a => Int -> a
+ Numeric.Units.Dimensional.Prelude: toInteger :: Integral a => a -> Integer
+ Numeric.Units.Dimensional.Prelude: toRational :: Real a => a -> Rational
+ Numeric.Units.Dimensional.Prelude: traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
+ Numeric.Units.Dimensional.Prelude: truncate :: (RealFrac a, Integral b) => a -> b
+ Numeric.Units.Dimensional.Prelude: type AmountOfSubstance = Quantity DAmountOfSubstance
+ Numeric.Units.Dimensional.Prelude: type Cbrt d = NRoot d 'Pos3
+ Numeric.Units.Dimensional.Prelude: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional.Prelude: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional.Prelude: type DMass = 'Dim 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type DOne = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type DTime = 'Dim 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Prelude: type Dimensionless = Quantity DOne
+ Numeric.Units.Dimensional.Prelude: type ElectricCurrent = Quantity DElectricCurrent
+ Numeric.Units.Dimensional.Prelude: type FilePath = String
+ Numeric.Units.Dimensional.Prelude: type IOError = IOException
+ Numeric.Units.Dimensional.Prelude: type KnownDimension (d :: Dimension) = HasDimension (Proxy d)
+ Numeric.Units.Dimensional.Prelude: type Length = Quantity DLength
+ Numeric.Units.Dimensional.Prelude: type LuminousIntensity = Quantity DLuminousIntensity
+ Numeric.Units.Dimensional.Prelude: type Mass = Quantity DMass
+ Numeric.Units.Dimensional.Prelude: type Quantity = SQuantity One
+ Numeric.Units.Dimensional.Prelude: type Rational = Ratio Integer
+ Numeric.Units.Dimensional.Prelude: type ReadS a = String -> [(a, String)]
+ Numeric.Units.Dimensional.Prelude: type Recip (d :: Dimension) = DOne / d
+ Numeric.Units.Dimensional.Prelude: type ShowS = String -> String
+ Numeric.Units.Dimensional.Prelude: type Sqrt d = NRoot d 'Pos2
+ Numeric.Units.Dimensional.Prelude: type String = [Char]
+ Numeric.Units.Dimensional.Prelude: type ThermodynamicTemperature = Quantity DThermodynamicTemperature
+ Numeric.Units.Dimensional.Prelude: type Time = Quantity DTime
+ Numeric.Units.Dimensional.Prelude: type Unit (m :: Metricality) = Dimensional ( 'DUnit m)
+ Numeric.Units.Dimensional.Prelude: type family (a :: Dimension) * (b :: Dimension)
+ Numeric.Units.Dimensional.Prelude: uncurry :: () => (a -> b -> c) -> (a, b) -> c
+ Numeric.Units.Dimensional.Prelude: undefined :: HasCallStack => a
+ Numeric.Units.Dimensional.Prelude: unlines :: [String] -> String
+ Numeric.Units.Dimensional.Prelude: until :: () => (a -> Bool) -> (a -> a) -> a -> a
+ Numeric.Units.Dimensional.Prelude: unwords :: [String] -> String
+ Numeric.Units.Dimensional.Prelude: unzip :: () => [(a, b)] -> ([a], [b])
+ Numeric.Units.Dimensional.Prelude: unzip3 :: () => [(a, b, c)] -> ([a], [b], [c])
+ Numeric.Units.Dimensional.Prelude: userError :: String -> IOError
+ Numeric.Units.Dimensional.Prelude: weaken :: Unit m d a -> Unit 'NonMetric d a
+ Numeric.Units.Dimensional.Prelude: words :: String -> [String]
+ Numeric.Units.Dimensional.Prelude: writeFile :: FilePath -> String -> IO ()
+ Numeric.Units.Dimensional.Prelude: zero :: Proxy Zero
+ Numeric.Units.Dimensional.Prelude: zip :: () => [a] -> [b] -> [(a, b)]
+ Numeric.Units.Dimensional.Prelude: zip3 :: () => [a] -> [b] -> [c] -> [(a, b, c)]
+ Numeric.Units.Dimensional.Prelude: zipWith :: () => (a -> b -> c) -> [a] -> [b] -> [c]
+ Numeric.Units.Dimensional.Prelude: zipWith3 :: () => (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
+ Numeric.Units.Dimensional.Prelude: }
+ Numeric.Units.Dimensional.Variants: infixl 7 *
+ Numeric.Units.Dimensional.Variants: type family Weaken (v :: Variant) :: Variant
- Numeric.Units.Dimensional: (*~) :: (Num a) => a -> Unit m d a -> Quantity d a
+ Numeric.Units.Dimensional: (*~) :: Num a => a -> Unit m d a -> Quantity d a
- Numeric.Units.Dimensional: _1 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _1 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _2 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _2 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _3 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _3 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _4 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _4 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _5 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _5 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _6 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _6 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _7 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _7 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _8 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _8 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: _9 :: (Num a) => Dimensionless a
+ Numeric.Units.Dimensional: _9 :: Num a => Dimensionless a
- Numeric.Units.Dimensional: asLens :: (Fractional a) => Unit m d a -> (forall f. Functor f => (a -> f a) -> Quantity d a -> f (Quantity d a))
+ Numeric.Units.Dimensional: asLens :: Fractional a => Unit m d a -> forall f. Functor f => (a -> f a) -> Quantity d a -> f (Quantity d a)
- Numeric.Units.Dimensional: atan2 :: (RealFloat a) => Quantity d a -> Quantity d a -> Dimensionless a
+ Numeric.Units.Dimensional: atan2 :: RealFloat a => Quantity d a -> Quantity d a -> Dimensionless a
- Numeric.Units.Dimensional: class KnownVariant (v :: Variant) where {
+ Numeric.Units.Dimensional: class KnownVariant (v :: Variant)
- Numeric.Units.Dimensional: data family Dimensional v :: Dimension -> * -> *;
+ Numeric.Units.Dimensional: data family Dimensional v :: Dimension -> Type -> Type
- Numeric.Units.Dimensional: recip :: (Fractional a) => Quantity d a -> Quantity (Recip d) a
+ Numeric.Units.Dimensional: recip :: Fractional a => Quantity d a -> Quantity (Recip d) a
- Numeric.Units.Dimensional: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
- Numeric.Units.Dimensional: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
- Numeric.Units.Dimensional: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.Coercion: coerce :: Coercible * a b => a -> b
+ Numeric.Units.Dimensional.Coercion: coerce :: Coercible a b => a -> b
- Numeric.Units.Dimensional.Dimensions.TermLevel: dynamicDimension :: (HasDynamicDimension a, (HasDimension a)) => a -> DynamicDimension
+ Numeric.Units.Dimensional.Dimensions.TermLevel: dynamicDimension :: (HasDynamicDimension a, HasDimension a) => a -> DynamicDimension
- Numeric.Units.Dimensional.Dimensions.TermLevel: hasSomeDimension :: (HasDynamicDimension a) => a -> Bool
+ Numeric.Units.Dimensional.Dimensions.TermLevel: hasSomeDimension :: HasDynamicDimension a => a -> Bool
- Numeric.Units.Dimensional.Dimensions.TermLevel: isCompatibleWith :: (HasDynamicDimension a) => a -> Dimension' -> Bool
+ Numeric.Units.Dimensional.Dimensions.TermLevel: isCompatibleWith :: HasDynamicDimension a => a -> Dimension' -> Bool
- Numeric.Units.Dimensional.Dimensions.TypeLevel: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
- Numeric.Units.Dimensional.Dimensions.TypeLevel: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Dimensions.TypeLevel: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Dimensions.TypeLevel: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
- Numeric.Units.Dimensional.Dimensions.TypeLevel: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.Dimensions.TypeLevel: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.Dynamic: (^) :: (Integral a) => AnyUnit -> a -> AnyUnit
+ Numeric.Units.Dimensional.Dynamic: (^) :: Integral a => AnyUnit -> a -> AnyUnit
- Numeric.Units.Dimensional.Dynamic: class Demotable (q :: * -> *)
+ Numeric.Units.Dimensional.Dynamic: class Demotable (q :: Type -> Type)
- Numeric.Units.Dimensional.Dynamic: class Promotable (q :: * -> *)
+ Numeric.Units.Dimensional.Dynamic: class Promotable (q :: Type -> Type)
- Numeric.Units.Dimensional.Dynamic: demoteUnit :: forall m d a. (KnownDimension d) => Unit m d a -> AnyUnit
+ Numeric.Units.Dimensional.Dynamic: demoteUnit :: forall m d a. KnownDimension d => Unit m d a -> AnyUnit
- Numeric.Units.Dimensional.Dynamic: demoteUnit' :: (KnownDimension d) => Unit m d ExactPi -> AnyUnit
+ Numeric.Units.Dimensional.Dynamic: demoteUnit' :: KnownDimension d => Unit m d ExactPi -> AnyUnit
- Numeric.Units.Dimensional.Dynamic: dynamicDimension :: (HasDynamicDimension a, (HasDimension a)) => a -> DynamicDimension
+ Numeric.Units.Dimensional.Dynamic: dynamicDimension :: (HasDynamicDimension a, HasDimension a) => a -> DynamicDimension
- Numeric.Units.Dimensional.Dynamic: polydimensionalZero :: (Num a) => DynQuantity a
+ Numeric.Units.Dimensional.Dynamic: polydimensionalZero :: Num a => DynQuantity a
- Numeric.Units.Dimensional.Dynamic: promoteUnit :: forall d. (KnownDimension d) => AnyUnit -> Maybe (Unit 'NonMetric d ExactPi)
+ Numeric.Units.Dimensional.Dynamic: promoteUnit :: forall d. KnownDimension d => AnyUnit -> Maybe (Unit 'NonMetric d ExactPi)
- Numeric.Units.Dimensional.FixedPoint: (+) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
+ Numeric.Units.Dimensional.FixedPoint: (+) :: Num a => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
- Numeric.Units.Dimensional.FixedPoint: (-) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
+ Numeric.Units.Dimensional.FixedPoint: (-) :: Num a => SQuantity s d a -> SQuantity s d a -> SQuantity s d a
- Numeric.Units.Dimensional.FixedPoint: abs :: (Num a) => SQuantity s d a -> SQuantity s d a
+ Numeric.Units.Dimensional.FixedPoint: abs :: Num a => SQuantity s d a -> SQuantity s d a
- Numeric.Units.Dimensional.FixedPoint: class KnownVariant (v :: Variant) where {
+ Numeric.Units.Dimensional.FixedPoint: class KnownVariant (v :: Variant)
- Numeric.Units.Dimensional.FixedPoint: data family Dimensional v :: Dimension -> * -> *;
+ Numeric.Units.Dimensional.FixedPoint: data family Dimensional v :: Dimension -> Type -> Type
- Numeric.Units.Dimensional.FixedPoint: epsilon :: (Integral a) => SQuantity s d a
+ Numeric.Units.Dimensional.FixedPoint: epsilon :: Integral a => SQuantity s d a
- Numeric.Units.Dimensional.FixedPoint: negate :: (Num a) => SQuantity s d a -> SQuantity s d a
+ Numeric.Units.Dimensional.FixedPoint: negate :: Num a => SQuantity s d a -> SQuantity s d a
- Numeric.Units.Dimensional.FixedPoint: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional.FixedPoint: type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
- Numeric.Units.Dimensional.FixedPoint: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.FixedPoint: type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.FixedPoint: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.FixedPoint: type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.FixedPoint: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional.FixedPoint: type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
- Numeric.Units.Dimensional.FixedPoint: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.FixedPoint: type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.NonSI: acre :: (Fractional a) => Unit 'NonMetric DArea a
+ Numeric.Units.Dimensional.NonSI: acre :: Fractional a => Unit 'NonMetric DArea a
- Numeric.Units.Dimensional.NonSI: angstrom :: (Fractional a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: angstrom :: Fractional a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.NonSI: atmosphere :: (Num a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: atmosphere :: Num a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: bar :: (Num a) => Unit 'Metric DPressure a
+ Numeric.Units.Dimensional.NonSI: bar :: Num a => Unit 'Metric DPressure a
- Numeric.Units.Dimensional.NonSI: degreeFahrenheit :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a
+ Numeric.Units.Dimensional.NonSI: degreeFahrenheit :: Fractional a => Unit 'NonMetric DThermodynamicTemperature a
- Numeric.Units.Dimensional.NonSI: degreeRankine :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a
+ Numeric.Units.Dimensional.NonSI: degreeRankine :: Fractional a => Unit 'NonMetric DThermodynamicTemperature a
- Numeric.Units.Dimensional.NonSI: gauss :: (Fractional a) => Unit 'NonMetric DMagneticFluxDensity a
+ Numeric.Units.Dimensional.NonSI: gauss :: Fractional a => Unit 'NonMetric DMagneticFluxDensity a
- Numeric.Units.Dimensional.NonSI: imperialCup :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialCup :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: imperialFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialFluidOunce :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: imperialGallon :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialGallon :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: imperialGill :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialGill :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: imperialPint :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialPint :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: imperialQuart :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: imperialQuart :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: inHg :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: inHg :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: inHg_NIST :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: inHg_NIST :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: inHg_UCUM :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: inHg_UCUM :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: knot :: (Fractional a) => Unit 'NonMetric DVelocity a
+ Numeric.Units.Dimensional.NonSI: knot :: Fractional a => Unit 'NonMetric DVelocity a
- Numeric.Units.Dimensional.NonSI: mile :: (Fractional a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: mile :: Fractional a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.NonSI: mmHg :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: mmHg :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: nauticalMile :: (Num a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: nauticalMile :: Num a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.NonSI: rad :: (Fractional a) => Unit 'Metric DAbsorbedDose a
+ Numeric.Units.Dimensional.NonSI: rad :: Fractional a => Unit 'Metric DAbsorbedDose a
- Numeric.Units.Dimensional.NonSI: revolution :: (Floating a) => Unit 'NonMetric DOne a
+ Numeric.Units.Dimensional.NonSI: revolution :: Floating a => Unit 'NonMetric DOne a
- Numeric.Units.Dimensional.NonSI: solid :: (Floating a) => Unit 'NonMetric DOne a
+ Numeric.Units.Dimensional.NonSI: solid :: Floating a => Unit 'NonMetric DOne a
- Numeric.Units.Dimensional.NonSI: stokes :: (Fractional a) => Unit 'Metric DKinematicViscosity a
+ Numeric.Units.Dimensional.NonSI: stokes :: Fractional a => Unit 'Metric DKinematicViscosity a
- Numeric.Units.Dimensional.NonSI: teaspoon :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: teaspoon :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: technicalAtmosphere :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: technicalAtmosphere :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: torr :: (Fractional a) => Unit 'NonMetric DPressure a
+ Numeric.Units.Dimensional.NonSI: torr :: Fractional a => Unit 'NonMetric DPressure a
- Numeric.Units.Dimensional.NonSI: usCup :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usCup :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usFluidOunce :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usGallon :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usGallon :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usGill :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usGill :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usPint :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usPint :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usQuart :: (Fractional a) => Unit 'NonMetric DVolume a
+ Numeric.Units.Dimensional.NonSI: usQuart :: Fractional a => Unit 'NonMetric DVolume a
- Numeric.Units.Dimensional.NonSI: usSurveyAcre :: (Fractional a) => Unit 'NonMetric DArea a
+ Numeric.Units.Dimensional.NonSI: usSurveyAcre :: Fractional a => Unit 'NonMetric DArea a
- Numeric.Units.Dimensional.NonSI: usSurveyMile :: (Fractional a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: usSurveyMile :: Fractional a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.NonSI: usSurveyYard :: (Fractional a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: usSurveyYard :: Fractional a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.NonSI: yard :: (Fractional a) => Unit 'NonMetric DLength a
+ Numeric.Units.Dimensional.NonSI: yard :: Fractional a => Unit 'NonMetric DLength a
- Numeric.Units.Dimensional.Quantities: type DAbsorbedDose = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAbsorbedDose = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DAbsorbedDoseRate = 'Dim 'Pos2 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAbsorbedDoseRate = 'Dim 'Pos2 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DAcceleration = 'Dim 'Pos1 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAcceleration = 'Dim 'Pos1 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DAmountOfSubstanceConcentration = 'Dim 'Neg3 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAmountOfSubstanceConcentration = 'Dim 'Neg3 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero
- Numeric.Units.Dimensional.Quantities: type DAngularAcceleration = 'Dim 'Zero 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAngularAcceleration = 'Dim 'Zero 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DCapacitance = 'Dim 'Neg2 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DCapacitance = 'Dim 'Neg2 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DCatalyticActivity = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Pos1 'Zero
+ Numeric.Units.Dimensional.Quantities: type DCatalyticActivity = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Pos1 'Zero
- Numeric.Units.Dimensional.Quantities: type DCurrentDensity = 'Dim 'Neg2 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DCurrentDensity = 'Dim 'Neg2 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DDynamicViscosity = 'Dim 'Neg1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DDynamicViscosity = 'Dim 'Neg1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricCharge = 'Dim 'Zero 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricCharge = 'Dim 'Zero 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricChargeDensity = 'Dim 'Neg3 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricChargeDensity = 'Dim 'Neg3 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricConductance = 'Dim 'Neg2 'Neg1 'Pos3 'Pos2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricConductance = 'Dim 'Neg2 'Neg1 'Pos3 'Pos2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricFieldStrength = 'Dim 'Pos1 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricFieldStrength = 'Dim 'Pos1 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricFluxDensity = 'Dim 'Neg2 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricFluxDensity = 'Dim 'Neg2 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricPotential = 'Dim 'Pos2 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricPotential = 'Dim 'Pos2 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DElectricResistance = 'Dim 'Pos2 'Pos1 'Neg3 'Neg2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DElectricResistance = 'Dim 'Pos2 'Pos1 'Neg3 'Neg2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DExposure = 'Dim 'Zero 'Neg1 'Pos1 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DExposure = 'Dim 'Zero 'Neg1 'Pos1 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DFirstMassMoment = 'Dim 'Pos1 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DFirstMassMoment = 'Dim 'Pos1 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DForce = 'Dim 'Pos1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DForce = 'Dim 'Pos1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DFrequency = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DFrequency = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DGravitationalParameter = 'Dim 'Pos3 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DGravitationalParameter = 'Dim 'Pos3 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DHeatCapacity = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DHeatCapacity = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DHeatFluxDensity = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DHeatFluxDensity = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DHeatTransferCoefficient = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DHeatTransferCoefficient = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DIlluminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional.Quantities: type DIlluminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
- Numeric.Units.Dimensional.Quantities: type DImpulse = 'Dim 'Pos1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DImpulse = 'Dim 'Pos1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DInductance = 'Dim 'Pos2 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DInductance = 'Dim 'Pos2 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DKinematicViscosity = 'Dim 'Pos2 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DKinematicViscosity = 'Dim 'Pos2 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DLuminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
+ Numeric.Units.Dimensional.Quantities: type DLuminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1
- Numeric.Units.Dimensional.Quantities: type DMagneticFieldStrength = 'Dim 'Neg1 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMagneticFieldStrength = 'Dim 'Neg1 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DMagneticFlux = 'Dim 'Pos2 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMagneticFlux = 'Dim 'Pos2 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DMagneticFluxDensity = 'Dim 'Zero 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMagneticFluxDensity = 'Dim 'Zero 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DMassDensity = 'Dim 'Neg3 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMassDensity = 'Dim 'Neg3 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DMassFlow = 'Dim 'Zero 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMassFlow = 'Dim 'Zero 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DMolarEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Neg1 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMolarEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Neg1 'Zero
- Numeric.Units.Dimensional.Quantities: type DMolarEntropy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Neg1 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMolarEntropy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Neg1 'Zero
- Numeric.Units.Dimensional.Quantities: type DMomentOfInertia = 'Dim 'Pos2 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DMomentOfInertia = 'Dim 'Pos2 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DPermeability = 'Dim 'Pos1 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DPermeability = 'Dim 'Pos1 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DPermittivity = 'Dim 'Neg3 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DPermittivity = 'Dim 'Neg3 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DPower = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DPower = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DPressure = 'Dim 'Neg1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DPressure = 'Dim 'Neg1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DSpecificHeatCapacity = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Neg1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DSpecificHeatCapacity = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Neg1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DSpecificVolume = 'Dim 'Pos3 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DSpecificVolume = 'Dim 'Pos3 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DSurfaceTension = 'Dim 'Zero 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DSurfaceTension = 'Dim 'Zero 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DThermalConductance = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DThermalConductance = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DThermalConductivity = 'Dim 'Pos1 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DThermalConductivity = 'Dim 'Pos1 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DThermalInsulance = 'Dim 'Zero 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DThermalInsulance = 'Dim 'Zero 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DThermalResistance = 'Dim 'Neg2 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DThermalResistance = 'Dim 'Neg2 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DThermalResistivity = 'Dim 'Neg1 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DThermalResistivity = 'Dim 'Neg1 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DVelocity = 'Dim 'Pos1 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DVelocity = 'Dim 'Pos1 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DVolume = 'Dim 'Pos3 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DVolume = 'Dim 'Pos3 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DVolumeFlow = 'Dim 'Pos3 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DVolumeFlow = 'Dim 'Pos3 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.Quantities: type DWaveNumber = 'Dim 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
+ Numeric.Units.Dimensional.Quantities: type DWaveNumber = 'Dim 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero
- Numeric.Units.Dimensional.SIUnits: applyPrefix :: (Fractional a) => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a
+ Numeric.Units.Dimensional.SIUnits: applyPrefix :: Fractional a => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a
Files
- CHANGELOG.md +4/−0
- LICENSE +31/−31
- Setup.lhs +2/−2
- benchmarks/Main.hs +19/−19
- dimensional.cabal +8/−3
- examples/GM.lhs +94/−94
- examples/ReadmeExample.hs +33/−33
- src/Numeric/Units/Dimensional.hs +788/−788
- src/Numeric/Units/Dimensional/Coercion.hs +35/−35
- src/Numeric/Units/Dimensional/Dimensions.hs +25/−25
- src/Numeric/Units/Dimensional/Dimensions/TermLevel.hs +192/−192
- src/Numeric/Units/Dimensional/Dimensions/TypeLevel.hs +160/−160
- src/Numeric/Units/Dimensional/Dynamic.hs +352/−351
- src/Numeric/Units/Dimensional/FixedPoint.hs +373/−373
- src/Numeric/Units/Dimensional/Float.hs +178/−178
- src/Numeric/Units/Dimensional/Functor.hs +41/−41
- src/Numeric/Units/Dimensional/Internal.hs +275/−274
- src/Numeric/Units/Dimensional/NonSI.hs +891/−891
- src/Numeric/Units/Dimensional/Prelude.hs +52/−52
- src/Numeric/Units/Dimensional/Quantities.hs +435/−435
- src/Numeric/Units/Dimensional/SIUnits.hs +315/−315
- src/Numeric/Units/Dimensional/UnitNames.hs +37/−37
- src/Numeric/Units/Dimensional/UnitNames/InterchangeNames.hs +41/−41
- src/Numeric/Units/Dimensional/UnitNames/Internal.hs +361/−361
- src/Numeric/Units/Dimensional/Variants.hs +94/−94
- tests/DocTests.hs +17/−10
- tests/Numeric/Units/Dimensional/DynamicSpec.hs +157/−157
- tests/Numeric/Units/Dimensional/QuantitiesSpec.hs +137/−137
- tests/Numeric/Units/DimensionalSpec.hs +48/−48
- tests/Spec.hs +4/−4
CHANGELOG.md view
@@ -1,3 +1,7 @@+1.2 (2018-11)+-------------+* Add `NoStarIsType` extension and import `Data.Kind.Type` for [GHC 8.6 compitbility](https://github.com/ghc-proposals/ghc-proposals/blob/05721788de9ab6538def68c3c2c9dec50c9f24a8/proposals/0020-no-type-in-type.rst). Abandon compatibility with GHC < 8.+ 1.1 (2018-03) ------------- * Added `Semigroup` instances for [GHC 8.4 compatibility](https://ghc.haskell.org/trac/ghc/wiki/Migration/8.4#SemigroupMonoidsuperclasses).
LICENSE view
@@ -1,31 +1,31 @@-Copyright (c) 2006-2018, Bjorn Buckwalter.-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions-are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of the copyright holder(s) nor the names of- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2006-2018, Bjorn Buckwalter. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the copyright holder(s) nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.
Setup.lhs view
@@ -1,3 +1,3 @@-#!/usr/bin/env runhaskell-> import Distribution.Simple+#!/usr/bin/env runhaskell +> import Distribution.Simple > main = defaultMain
benchmarks/Main.hs view
@@ -1,19 +1,19 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Main where--import Criterion.Main-import Numeric.Units.Dimensional.Prelude-import qualified Prelude as P--main :: IO ()-main = defaultMain [- bench "RawArithmetic" $ nf rawArithmetic 1000- , bench "Arithmetic" $ nf arithmetic 1000- ]--rawArithmetic :: Int -> [Double]-rawArithmetic n = fmap (P./ 3.7) $ [1.0 .. fromIntegral n]--arithmetic :: Int -> [Density Double]-arithmetic n = fmap (/ (3.7 *~ cubic meter)) $ [1.0 .. fromIntegral n] *~~ kilo gram+{-# LANGUAGE NoImplicitPrelude #-} + +module Main where + +import Criterion.Main +import Numeric.Units.Dimensional.Prelude +import qualified Prelude as P + +main :: IO () +main = defaultMain [ + bench "RawArithmetic" $ nf rawArithmetic 1000 + , bench "Arithmetic" $ nf arithmetic 1000 + ] + +rawArithmetic :: Int -> [Double] +rawArithmetic n = fmap (P./ 3.7) $ [1.0 .. fromIntegral n] + +arithmetic :: Int -> [Density Double] +arithmetic n = fmap (/ (3.7 *~ cubic meter)) $ [1.0 .. fromIntegral n] *~~ kilo gram
dimensional.cabal view
@@ -1,5 +1,5 @@ name: dimensional-version: 1.1+version: 1.2 license: BSD3 license-file: LICENSE copyright: Bjorn Buckwalter 2006-2018@@ -12,7 +12,7 @@ synopsis: Statically checked physical dimensions, using Type Families and Data Kinds. cabal-version: >= 1.10-tested-with: GHC == 7.8.4, GHC == 7.10.1, GHC == 7.10.2, GHC == 7.10.3, GHC == 8.0.1, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1+tested-with: GHC == 8.0.1, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.1 build-type: Simple description:@@ -30,7 +30,7 @@ rather than functional dependencies. This enables a number of features, including improved support for unit names and quantities with statically-unknown dimensions. - Requires GHC 7.8 or later.+ Requires GHC 8.0 or later. extra-source-files: README.md, CHANGELOG.md,@@ -52,6 +52,8 @@ hs-source-dirs: src default-language: Haskell2010 default-extensions: NoImplicitPrelude+ if impl(ghc >= 8.6)+ default-extensions: NoStarIsType ghc-options: -Wall exposed-modules: Numeric.Units.Dimensional, Numeric.Units.Dimensional.Coercion,@@ -78,6 +80,8 @@ hs-source-dirs: tests default-language: Haskell2010 default-extensions: NoImplicitPrelude+ if impl(ghc >= 8.6)+ default-extensions: NoStarIsType other-modules: Numeric.Units.DimensionalSpec Numeric.Units.Dimensional.DynamicSpec Numeric.Units.Dimensional.QuantitiesSpec@@ -86,6 +90,7 @@ hspec, QuickCheck, base+ build-tool-depends: hspec-discover:hspec-discover == 2.* test-suite doctests type: exitcode-stdio-1.0
examples/GM.lhs view
@@ -1,94 +1,94 @@--= GM calculation =--Several representation can be used to describe a satellite's orbit. Two-of the most popular are the cartesian state vector (position and-velocity vectors) and the keplerian elements. Conversion between the two-representations is fairly straight-forward but requires an assumption-to be made about the universal gravitational constant 'G' and the mass-'M' of the body the satellite is orbiting. In practice they are often-combined into a parameter "mu = GM" where the magnitude of 'mu' is-empirically better known that the magnitudes of 'G' and 'M' individually.--*The problem:* Given two representations of the same satellite orbit -- one-using the cartesian state vector and using keplerian elements, both at the-same epoch -- determine the value of 'mu' used to convert between the two.-{{{--> {-# LANGUAGE NegativeLiterals #-}-> module GM where--> import Numeric.Units.Dimensional.Prelude-> import qualified Prelude--}}}-The state vector describing the orbit at epoch.-{{{--> x = 4383.9449203752 *~ kilo meter-> y = -41940.917505092 *~ kilo meter-> z = 22.790255916589 *~ kilo meter-> x_dot = 3.0575666627812 *~ (kilo meter / second)-> y_dot = 0.32047068607303 *~ (kilo meter / second)-> z_dot = 0.00084729371755294 *~ (kilo meter / second)--}}}-From the state vector we calculate the distance from the reference frame center at epoch and the velocity squared at epoch.-{{{--> r = sqrt (x ^ pos2 + y ^ pos2 + z ^ pos2)-> v = sqrt (x_dot ^ pos2 + y_dot ^ pos2 + z_dot ^ pos2)--}}}-The kinetic energy per unit mass at epoch is a function of the velocity.-{{{--> e_kin :: EnergyPerUnitMass Double-> e_kin = v ^ pos2 / _2--}}}-The only keplerian element we need for this calculation is the semi-major axis.-{{{--> semi_major_axis = 42165.221455 *~ kilo meter--}}}-The expression for 'mu' is obtained by solving the following equation system:-- e_pot = - mu / r,-- e_tot = - mu / 2a,-- e_tot = e_pot + e_kin,--which gives:-- mu = e_kin / (1 / r - 1 / 2a).--{{{--> mu = e_kin / (_1 / r - _1 / (_2 * semi_major_axis))--}}}-Wrap up with a main function showing the value of 'mu' in desired units.-{{{--> main = putStrLn $ "The value used for GM was " ++ show mu--}}}-Loading this module in 'ghci' and running 'main' produces the following output.-{{{- ___ ___ _- / _ \ /\ /\/ __(_)- / /_\// /_/ / / | | GHC Interactive, version 6.6.1, for Haskell 98.-/ /_\\/ __ / /___| | http://www.haskell.org/ghc/-\____/\/ /_/\____/|_| Type :? for help.--Loading package base ... linking ... done.-[1 of 1] Compiling GM ( GM.lhs, interpreted )-Ok, modules loaded: GM.-*GM> main-Loading package dimensional-0.5 ... linking ... done.-The value used for GM was 3.986004400008003e14 m^3 s^-2-*GM>-}}}+ += GM calculation = + +Several representation can be used to describe a satellite's orbit. Two +of the most popular are the cartesian state vector (position and +velocity vectors) and the keplerian elements. Conversion between the two +representations is fairly straight-forward but requires an assumption +to be made about the universal gravitational constant 'G' and the mass +'M' of the body the satellite is orbiting. In practice they are often +combined into a parameter "mu = GM" where the magnitude of 'mu' is +empirically better known that the magnitudes of 'G' and 'M' individually. + +*The problem:* Given two representations of the same satellite orbit -- one +using the cartesian state vector and using keplerian elements, both at the +same epoch -- determine the value of 'mu' used to convert between the two. +{{{ + +> {-# LANGUAGE NegativeLiterals #-} +> module GM where + +> import Numeric.Units.Dimensional.Prelude +> import qualified Prelude + +}}} +The state vector describing the orbit at epoch. +{{{ + +> x = 4383.9449203752 *~ kilo meter +> y = -41940.917505092 *~ kilo meter +> z = 22.790255916589 *~ kilo meter +> x_dot = 3.0575666627812 *~ (kilo meter / second) +> y_dot = 0.32047068607303 *~ (kilo meter / second) +> z_dot = 0.00084729371755294 *~ (kilo meter / second) + +}}} +From the state vector we calculate the distance from the reference frame center at epoch and the velocity squared at epoch. +{{{ + +> r = sqrt (x ^ pos2 + y ^ pos2 + z ^ pos2) +> v = sqrt (x_dot ^ pos2 + y_dot ^ pos2 + z_dot ^ pos2) + +}}} +The kinetic energy per unit mass at epoch is a function of the velocity. +{{{ + +> e_kin :: EnergyPerUnitMass Double +> e_kin = v ^ pos2 / _2 + +}}} +The only keplerian element we need for this calculation is the semi-major axis. +{{{ + +> semi_major_axis = 42165.221455 *~ kilo meter + +}}} +The expression for 'mu' is obtained by solving the following equation system: + + e_pot = - mu / r, + + e_tot = - mu / 2a, + + e_tot = e_pot + e_kin, + +which gives: + + mu = e_kin / (1 / r - 1 / 2a). + +{{{ + +> mu = e_kin / (_1 / r - _1 / (_2 * semi_major_axis)) + +}}} +Wrap up with a main function showing the value of 'mu' in desired units. +{{{ + +> main = putStrLn $ "The value used for GM was " ++ show mu + +}}} +Loading this module in 'ghci' and running 'main' produces the following output. +{{{ + ___ ___ _ + / _ \ /\ /\/ __(_) + / /_\// /_/ / / | | GHC Interactive, version 6.6.1, for Haskell 98. +/ /_\\/ __ / /___| | http://www.haskell.org/ghc/ +\____/\/ /_/\____/|_| Type :? for help. + +Loading package base ... linking ... done. +[1 of 1] Compiling GM ( GM.lhs, interpreted ) +Ok, modules loaded: GM. +*GM> main +Loading package dimensional-0.5 ... linking ... done. +The value used for GM was 3.986004400008003e14 m^3 s^-2 +*GM> +}}}
examples/ReadmeExample.hs view
@@ -1,33 +1,33 @@-{-# LANGUAGE NoImplicitPrelude #-}--module ReadmeExample where--import Numeric.Units.Dimensional.Prelude-import Numeric.Units.Dimensional.NonSI (mile)--leg :: Length Double-leg = 1 *~ mile -- *~ combines a raw number and a unit to form a quantity--speeds :: [Velocity Double]-speeds = [60, 50, 40, 30] *~~ (kilo meter / hour)- -- *~~ does the same thing for a whole Functor at once- -- Parentheses are required around unit expressions that are comingled with *~, /~, *~~, or /~~ operations--timeOfJourney :: Time Double-timeOfJourney = sum $ fmap (leg /) speeds- -- We can use dimensional versions of ordinary functions like / and sum to combine quantities--averageSpeed :: Velocity Double-averageSpeed = _4 * leg / timeOfJourney- -- _4 is an alias for the dimensionless number 4--wholeSeconds :: Integer-wholeSeconds = ceiling $ timeOfJourney /~ second- -- /~ lets us recover a raw number from a quantity and a unit in which it should be expressed--main :: IO ()-main = do- putStrLn $ "Length of journey is: " ++ showIn minute timeOfJourney- putStrLn $ "Average speed is: " ++ showIn (mile / hour) averageSpeed- putStrLn $ "If we don't want to be explicit about units, the show instance uses the SI basis: " ++ show averageSpeed- putStrLn $ "The journey requires " ++ show wholeSeconds ++ " seconds, rounded up to the nearest second."+{-# LANGUAGE NoImplicitPrelude #-} + +module ReadmeExample where + +import Numeric.Units.Dimensional.Prelude +import Numeric.Units.Dimensional.NonSI (mile) + +leg :: Length Double +leg = 1 *~ mile -- *~ combines a raw number and a unit to form a quantity + +speeds :: [Velocity Double] +speeds = [60, 50, 40, 30] *~~ (kilo meter / hour) + -- *~~ does the same thing for a whole Functor at once + -- Parentheses are required around unit expressions that are comingled with *~, /~, *~~, or /~~ operations + +timeOfJourney :: Time Double +timeOfJourney = sum $ fmap (leg /) speeds + -- We can use dimensional versions of ordinary functions like / and sum to combine quantities + +averageSpeed :: Velocity Double +averageSpeed = _4 * leg / timeOfJourney + -- _4 is an alias for the dimensionless number 4 + +wholeSeconds :: Integer +wholeSeconds = ceiling $ timeOfJourney /~ second + -- /~ lets us recover a raw number from a quantity and a unit in which it should be expressed + +main :: IO () +main = do + putStrLn $ "Length of journey is: " ++ showIn minute timeOfJourney + putStrLn $ "Average speed is: " ++ showIn (mile / hour) averageSpeed + putStrLn $ "If we don't want to be explicit about units, the show instance uses the SI basis: " ++ show averageSpeed + putStrLn $ "The journey requires " ++ show wholeSeconds ++ " seconds, rounded up to the nearest second."
src/Numeric/Units/Dimensional.hs view
@@ -1,788 +1,788 @@-{-# OPTIONS_HADDOCK show-extensions #-}--{-# LANGUAGE AutoDeriveTypeable #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE PatternGuards #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RoleAnnotations #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}---{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--= Summary--In this module we provide data types for performing arithmetic with-physical quantities and units. Information about the physical-dimensions of the quantities/units is embedded in their types and-the validity of operations is verified by the type checker at compile-time. The boxing and unboxing of numerical values as quantities is-done by multiplication and division of units, of which an incomplete-set is provided.--We limit ourselves to \"Newtonian\" physics. We do not attempt to-accommodate relativistic physics in which e.g. addition of length-and time would be valid.--As far as possible and/or practical the conventions and guidelines-of NIST's "Guide for the Use of the International System of Units-(SI)" <#note1 [1]> are followed. Occasionally we will reference specific-sections from the guide and deviations will be explained.--== Disclaimer--Merely an engineer, the author doubtlessly uses a language and-notation that makes mathematicians and physicist cringe. He does-not mind constructive criticism (or pull requests).--The sets of functions and units defined herein are incomplete and-reflect only the author's needs to date. Again, patches are welcome.--= Usage--== Preliminaries--This module requires GHC 7.8 or later. We utilize Data Kinds, TypeNats,-Closed Type Families, etc. Clients of the module are generally not-required to use these extensions.--Clients probably will want to use the NegativeLiterals extension.--== Examples--We have defined operators and units that allow us to define and-work with physical quantities. A physical quantity is defined by-multiplying a number with a unit (the type signature is optional).--> v :: Velocity Prelude.Double-> v = 90 *~ (kilo meter / hour)--It follows naturally that the numerical value of a quantity is-obtained by division by a unit.--> numval :: Prelude.Double-> numval = v /~ (meter / second)--The notion of a quantity as the product of a numerical value and a-unit is supported by 7.1 "Value and numerical value of a quantity" of-<#note1 [1]>. While the above syntax is fairly natural it is unfortunate that-it must violate a number of the guidelines in <#note1 [1]>, in particular 9.3-"Spelling unit names with prefixes", 9.4 "Spelling unit names obtained-by multiplication", 9.5 "Spelling unit names obtained by division".--As a more elaborate example of how to use the module we define a-function for calculating the escape velocity of a celestial body-<#note2 [2]>.--> escapeVelocity :: (Floating a) => Mass a -> Length a -> Velocity a-> escapeVelocity m r = sqrt (two * g * m / r)-> where-> two = 2 *~ one-> g = 6.6720e-11 *~ (newton * meter ^ pos2 / kilo gram ^ pos2)--For completeness we should also show an example of the error messages-we will get from GHC when performing invalid arithmetic. In the-best case GHC will be able to use the type synonyms we have defined-in its error messages.--> let x = 1 *~ meter + 1 *~ second->-> Couldn't match type 'Numeric.NumType.DK.Integers.Zero-> with 'Numeric.NumType.DK.Integers.Pos1-> Expected type: Unit 'Metric DLength a-> Actual type: Unit 'Metric DTime a-> In the second argument of `(*~)', namely `second'-> In the second argument of `(+)', namely `1 *~ second'--In other cases the error messages aren't very friendly.--> let x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram->-> Couldn't match type 'Numeric.NumType.DK.Integers.Zero-> with 'Numeric.NumType.DK.Integers.Neg1-> Expected type: Quantity DMass a-> Actual type: Dimensional-> ('DQuantity V.* 'DQuantity) (DLength / DTime) a-> In the first argument of `(+)', namely `1 *~ meter / (1 *~ second)'-> In the expression: 1 *~ meter / (1 *~ second) + 1 *~ kilo gram-> In an equation for `x':-> x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram--It is the author's experience that the usefulness of the compiler-error messages is more often than not limited to pinpointing the-location of errors.--= Notes--== Future work--While there is an insane amount of units in use around the world-it is reasonable to provide those in relatively widespread use. Units outside-of SI will most likely be added on an as-needed basis.--Additional physics models could be implemented. See <#note3 [3]> for ideas.--== Related work--Henning Thielemann numeric prelude has a physical units library,-however, checking of dimensions is dynamic rather than static.-Aaron Denney has created a toy example of statically checked-physical dimensions covering only length and time. HaskellWiki-has pointers <#note4 [4]> to these.--Also see Samuel Hoffstaetter's blog post <#note5 [5]> which uses techniques-similar to this library.--Libraries with similar functionality exist for other programming-languages and may serve as inspiration. The author has found the-Java library JScience <#note6 [6]> and the Fortress programming language <#note7 [7]>-particularly noteworthy.--== References--1. #note1# http://physics.nist.gov/Pubs/SP811/-2. #note2# http://en.wikipedia.org/wiki/Escape_velocity-3. #note3# http://jscience.org/api/org/jscience/physics/models/package-summary.html-4. #note4# http://www.haskell.org/haskellwiki/Physical_units-5. #note5# http://liftm.wordpress.com/2007/06/03/scientificdimension-type-arithmetic-and-physical-units-in-haskell/-6. #note6# http://jscience.org/-7. #note7# http://research.sun.com/projects/plrg/fortress.pdf---}--module Numeric.Units.Dimensional- (- -- * Types- -- $types- Dimensional,- Unit, Quantity,- Metricality(..),- -- * Physical Dimensions- -- $dimensions- Dimension (Dim),- -- ** Dimension Arithmetic- -- $dimension-arithmetic- type (*), type (/), type (^), NRoot, Sqrt, Cbrt, Recip,- -- ** Term Level Representation of Dimensions- -- $dimension-terms- Dimension' (Dim'), HasDimension(..), KnownDimension,- -- * Dimensional Arithmetic- (*~), (/~),- (^), (^/), (**), (*), (/), (+), (-),- negate, abs, signum, recip, nroot, sqrt, cbrt,- -- ** Transcendental Functions- exp, log, logBase, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, atan2,- -- ** Operations on Collections- -- $collections- (*~~), (/~~), sum, mean, product, dimensionlessLength, nFromTo,- -- * Dimension Synonyms- -- $dimension-synonyms- DOne, DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity,- -- * Quantity Synonyms- -- $quantity-synonyms- Dimensionless, Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity,- -- * Constants- -- $constants- _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau,- -- * Constructing Units- siUnit, one, mkUnitR, mkUnitQ, mkUnitZ,- -- * Unit Metadata- name, exactValue, weaken, strengthen, exactify,- -- * Pretty Printing- showIn,- -- * On 'Functor', and Conversion Between Number Representations- -- $functor- KnownVariant(dmap), changeRep, changeRepApproximate,- -- * Lenses- -- $lenses- asLens- )- where--import Prelude- ( Eq(..), Num, Fractional, Floating, Real, RealFloat, Functor, fmap- , (.), flip, (++), fromIntegral, fromInteger, fromRational, error, max, succ- , Int, Integer, Integral, ($), uncurry, realToFrac, otherwise- )-import qualified Prelude-import Numeric.NumType.DK.Integers- ( pos2, pos3- , KnownTypeInt, toNum- )-import Data.Data-import Data.ExactPi-import Data.Foldable (Foldable(foldr))-import Data.Maybe-import Data.Ratio-import Numeric.Units.Dimensional.Dimensions-import Numeric.Units.Dimensional.Internal-import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen, product)-import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name-import Numeric.Units.Dimensional.Variants hiding (type (*), type (/))-import qualified Numeric.Units.Dimensional.Variants as V---- Provide a version of length which is compatible with base-4.8's version.--- Where 4.8 is available we use that version as it may have performance advantages.--- Where it is not available we implement it in terms of foldl'.-#if MIN_VERSION_base(4,8,0)-import Data.Foldable (Foldable(length))-#else-import Data.Foldable (Foldable(foldl'))--length :: Foldable t => t a -> Int-length = foldl' (\c _ -> c Prelude.+ 1) 0-#endif---- $setup--- >>> :set -XFlexibleInstances--- >>> :set -XNoImplicitPrelude--- >>> import Test.QuickCheck.Arbitrary--- >>> import Numeric.Units.Dimensional.Prelude--- >>> import Numeric.Units.Dimensional.Float--- >>> import Numeric.Units.Dimensional.NonSI--- >>> instance Arbitrary a => Arbitrary (Quantity d a) where arbitrary = fmap Quantity arbitrary--{--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.--}--infixr 8 ^, ^/, **-infixl 7 *, /-infixl 6 +, ----{- $types-Our primary objective is to define a data type that can be used to-represent (while still differentiating between) units and quantities.-There are two reasons for consolidating units and quantities in one-data type. The first being to allow code reuse as they are largely-subject to the same operations. The second being that it allows-reuse of operators (and functions) between the two without resorting-to occasionally cumbersome type classes.--The relationship between (the value of) a 'Quantity', its numerical-value and its 'Unit' is described in 7.1 "Value and numerical value-of a quantity" of <#note1 [1]>. In short a 'Quantity' is the product of a-number and a 'Unit'. We define the '*~' operator as a convenient-way to declare quantities as such a product.---}---- | Extracts the 'UnitName' of a 'Unit'.-name :: Unit m d a -> UnitName m-name (Unit n _ _) = n---- | Extracts the exact value of a 'Unit', expressed in terms of the SI coherent derived unit (see 'siUnit') of the same 'Dimension'.------ Note that the actual value may in some cases be approximate, for example if the unit is defined by experiment.-exactValue :: Unit m d a -> ExactPi-exactValue (Unit _ e _) = e---- | Discards potentially unwanted type level information about a 'Unit'.-weaken :: Unit m d a -> Unit 'NonMetric d a-weaken (Unit n e v) = Unit (Name.weaken n) e v---- | Attempts to convert a 'Unit' which may or may not be 'Metric' to one--- which is certainly 'Metric'.-strengthen :: Unit m d a -> Maybe (Unit 'Metric d a)-strengthen (Unit n e v) | Just n' <- Name.strengthen n = Just $ Unit n' e v- | otherwise = Nothing---- | Forms the exact version of a 'Unit'.-exactify :: Unit m d a -> Unit m d ExactPi-exactify (Unit n e _) = Unit n e e---- | Forms a 'Quantity' by multipliying a number and a unit.-(*~) :: (Num a) => a -> Unit m d a -> Quantity d a-x *~ (Unit _ _ y) = Quantity (x Prelude.* y)---- | Divides a 'Quantity' by a 'Unit' of the same physical dimension, obtaining the--- numerical value of the quantity expressed in that unit.-(/~) :: Fractional a => Quantity d a -> Unit m d a -> a-(Quantity x) /~ (Unit _ _ y) = (x Prelude./ y)--{--We give '*~' and '/~' the same fixity as '*' and '/' defined below.-Note that this necessitates the use of parenthesis when composing-units using '*' and '/', e.g. "1 *~ (meter / second)".--}--infixl 7 *~, /~--{- $dimensions-The phantom type variable d encompasses the physical dimension of-a 'Dimensional'. As detailed in <#note5 [5]> there are seven base dimensions,-which can be combined in integer powers to a given physical dimension.-We represent physical dimensions as the powers of the seven base-dimensions that make up the given dimension. The powers are represented-using NumTypes. For convenience we collect all seven base dimensions-in a data kind 'Dimension'.--We could have chosen to provide type variables for the seven base-dimensions in 'Dimensional' instead of creating a new data kind-'Dimension'. However, that would have made any type signatures involving-'Dimensional' very cumbersome. By encompassing the physical dimension-in a single type variable we can "hide" the cumbersome type arithmetic-behind convenient type classes as will be seen later.---}--{- $dimension-synonyms-Using our 'Dimension' data kind we define some type synonyms for convenience.-We start with the base dimensions, others can be found in "Numeric.Units.Dimensional.Quantities".---}--{- $quantity-synonyms-Using the above type synonyms we can define type synonyms for-quantities of particular physical dimensions.--Again we limit ourselves to the base dimensions, others can be found in "Numeric.Units.Dimensional.Quantities".---}--type Dimensionless = Quantity DOne-type Length = Quantity DLength-type Mass = Quantity DMass-type Time = Quantity DTime-type ElectricCurrent = Quantity DElectricCurrent-type ThermodynamicTemperature = Quantity DThermodynamicTemperature-type AmountOfSubstance = Quantity DAmountOfSubstance-type LuminousIntensity = Quantity DLuminousIntensity--{- $dimension-arithmetic-When performing arithmetic on units and quantities the arithmetics-must be applied to both the numerical values of the Dimensionals-but also to their physical dimensions. The type level arithmetic-on physical dimensions is governed by closed type families expressed-as type operators.--We could provide the 'Mul' and 'Div' classes with full functional-dependencies but that would be of limited utility as there is no-limited use for "backwards" type inference. Efforts are underway to-develop a type-checker plugin that does enable these scenarios, e.g.-for linear algebra.---}--{--= Arithmetic on units and quantities =--Thanks to the arithmetic on physical dimensions having been sorted-out separately a lot of the arithmetic on Dimensionals is straight-forward. In particular the type signatures are much simplified.--Multiplication, division and powers apply to both units and quantities.--}---- | Multiplies two 'Quantity's or two 'Unit's.------ The intimidating type signature captures the similarity between these operations--- and ensures that composite 'Unit's are 'NonMetric'.-(*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) (d1 * d2) a-(*) = liftD2 (Prelude.*) (Prelude.*) (Name.*)---- | Divides one 'Quantity' by another or one 'Unit' by another.------ The intimidating type signature captures the similarity between these operations--- and ensures that composite 'Unit's are 'NotPrefixable'.-(/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V./ v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V./ v2) (d1 / d2) a-(/) = liftD2 (Prelude./) (Prelude./) (Name./)---- | Forms the reciprocal of a 'Quantity', which has the reciprocal dimension.------ >>> recip $ 47 *~ hertz--- 2.127659574468085e-2 s-recip :: (Fractional a) => Quantity d a -> Quantity (Recip d) a-recip = liftQ Prelude.recip---- | Raises a 'Quantity' or 'Unit' to an integer power.------ Because the power chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation--- of the exponent in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ...--- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are--- also reexported by "Numeric.Units.Dimensional.Prelude".------ The intimidating type signature captures the similarity between these operations--- and ensures that composite 'Unit's are 'NotPrefixable'.-(^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v))- => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a-x ^ n = let n' = (toNum n) :: Int- in liftD (Prelude.^^ n') (Prelude.^^ n') (Name.^ n') x--{--A special case is that dimensionless quantities are not restricted-to integer exponents. This is accommodated by the '**' operator-defined later.---= Quantity operations =--Some additional operations obviously only make sense for quantities.-Of these, negation, addition and subtraction are particularly simple-as they are done in a single physical dimension.--}---- | Negates the value of a 'Quantity'.-negate :: Num a => Quantity d a -> Quantity d a-negate = liftQ Prelude.negate---- | Adds two 'Quantity's.-(+) :: Num a => Quantity d a -> Quantity d a -> Quantity d a-(+) = liftQ2 (Prelude.+)---- | Subtracts one 'Quantity' from another.-(-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a-(-) = liftQ2 (Prelude.-)---- | Takes the absolute value of a 'Quantity'.-abs :: Num a => Quantity d a -> Quantity d a-abs = liftQ Prelude.abs---- | Takes the sign of a 'Quantity'. The functions 'abs' and 'signum'--- satisy the law that:------ > abs x * signum x == x------ The sign is either @negate _1@ (negative), @_0@ (zero),--- or @_1@ (positive).-signum :: Num a => Quantity d a -> Dimensionless a-signum = liftQ Prelude.signum--{--Roots of arbitrary (integral) degree. Appears to occasionally be useful-for units as well as quantities.--}---- | Computes the nth root of a 'Quantity' using 'Prelude.**'.------ The 'NRoot' type family will prevent application of this operator where the result would have a fractional dimension or where n is zero.------ Because the root chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation--- of the root in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ...--- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are--- also reexported by "Numeric.Units.Dimensional.Prelude".------ n must not be zero. Negative roots are defined such that @nroot (Proxy :: Proxy (Negate n)) x == nroot (Proxy :: Proxy n) (recip x)@.------ Also available in operator form, see '^/'.-nroot :: (KnownTypeInt n, Floating a)- => Proxy n -> Quantity d a -> Quantity (NRoot d n) a-nroot n = let n' = 1 Prelude./ toNum n- in liftQ (Prelude.** n')--{--We provide short-hands for the square and cube roots.--}---- | Computes the square root of a 'Quantity' using 'Prelude.**'.------ The 'NRoot' type family will prevent application where the supplied quantity does not have a square dimension.------ prop> (x :: Area Double) >= _0 ==> sqrt x == nroot pos2 x-sqrt :: Floating a => Quantity d a -> Quantity (Sqrt d) a-sqrt = nroot pos2---- | Computes the cube root of a 'Quantity' using 'Prelude.**'.------ The 'NRoot' type family will prevent application where the supplied quantity does not have a cubic dimension.------ prop> (x :: Volume Double) >= _0 ==> cbrt x == nroot pos3 x-cbrt :: Floating a => Quantity d a -> Quantity (Cbrt d) a-cbrt = nroot pos3--{--We also provide an operator alternative to nroot for those that-prefer such.--}---- | Computes the nth root of a 'Quantity' using 'Prelude.**'.------ The 'NRoot' type family will prevent application of this operator where the result would have a fractional dimension or where n is zero.------ Because the root chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation--- of the root in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ...--- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are--- also reexported by "Numeric.Units.Dimensional.Prelude".------ Also available in prefix form, see 'nroot'.-(^/) :: (KnownTypeInt n, Floating a)- => Quantity d a -> Proxy n -> Quantity (NRoot d n) a-(^/) = flip nroot--{- $collections-Here we define operators and functions to make working with homogenuous-lists of dimensionals more convenient.--We define two convenience operators for applying units to all-elements of a functor (e.g. a list).--}---- | Applies '*~' to all values in a functor.-(*~~) :: (Functor f, Num a) => f a -> Unit m d a -> f (Quantity d a)-xs *~~ u = fmap (*~ u) xs---- | Applies '/~' to all values in a functor.-(/~~) :: forall f m d a.(Functor f, Fractional a) => f (Quantity d a) -> Unit m d a -> f a-xs /~~ u = fmap (/~ u) xs--infixl 7 *~~, /~~---- | The sum of all elements in a foldable structure.------ >>> sum ([] :: [Mass Double])--- 0.0 kg------ >>> sum [12.4 *~ meter, 1 *~ foot]--- 12.7048 m-sum :: (Num a, Foldable f) => f (Quantity d a) -> Quantity d a-sum = foldr (+) _0---- | The product of all elements in a foldable structure.------ >>> product ([] :: [Dimensionless Double])--- 1.0------ >>> product [pi, _4, 0.36 *~ one]--- 4.523893421169302-product :: (Num a, Foldable f) => f (Dimensionless a) -> Dimensionless a-product = foldr (*) _1---- | The arithmetic mean of all elements in a foldable structure.------ >>> mean [pi, _7]--- 5.070796326794897-mean :: (Fractional a, Foldable f) => f (Quantity d a) -> Quantity d a-mean = uncurry (/) . foldr accumulate (_0, _0)- where- accumulate val (accum, count) = (accum + val, count + _1)---- | The length of the foldable data structure as a 'Dimensionless'.--- This can be useful for purposes of e.g. calculating averages.------ >>> dimensionlessLength ["foo", "bar"]--- 2-dimensionlessLength :: (Num a, Foldable f) => f b -> Dimensionless a-dimensionlessLength x = (fromIntegral $ length x) *~ one---- | Returns a list of quantities between given bounds.------ prop> n <= 0 ==> nFromTo (x :: Mass Double) (y :: Mass Double) n == [x, y]------ prop> (x :: Length Double) <= (y :: Length Double) ==> all (\z -> x <= z && z <= y) (nFromTo x y n)------ >>> nFromTo _0 _3 2--- [0.0,1.0,2.0,3.0]------ >>> nFromTo _1 _0 7--- [1.0,0.875,0.75,0.625,0.5,0.375,0.25,0.125,0.0]------ >>> nFromTo _0 _1 (-5)--- [0.0,1.0]-nFromTo :: (Fractional a, Integral b) => Quantity d a -- ^ The initial value.- -> Quantity d a -- ^ The final value.- -> b -- ^ The number of intermediate values. If less than one, no intermediate values will result.- -> [Quantity d a]-nFromTo xi xf n = fmap f [0..n'] ++ [xf]- where- n' = max 0 n- f i = xi + realToFrac (i % succ n') *~ one * (xf - xi)--{--We continue by defining elementary functions on 'Dimensionless'-that may be obviously useful.--}--exp, log, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh- :: Floating a => Dimensionless a -> Dimensionless a-exp = fmap Prelude.exp-log = fmap Prelude.log-sin = fmap Prelude.sin-cos = fmap Prelude.cos-tan = fmap Prelude.tan-asin = fmap Prelude.asin-acos = fmap Prelude.acos-atan = fmap Prelude.atan-sinh = fmap Prelude.sinh-cosh = fmap Prelude.cosh-tanh = fmap Prelude.tanh-asinh = fmap Prelude.asinh-acosh = fmap Prelude.acosh-atanh = fmap Prelude.atanh---- | Raises a dimensionless quantity to a dimensionless power.-(**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a-(**) = liftQ2 (Prelude.**)---- | Takes the logarithm of the second argument in the base of the first.------ >>> logBase _2 _8--- 3.0-logBase :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a-logBase = liftQ2 Prelude.logBase---- | The standard two argument arctangent function.--- Since it interprets its two arguments in comparison with one another, the input may have any dimension.------ >>> atan2 _0 _1--- 0.0------ >>> atan2 _1 _0--- 1.5707963267948966------ >>> atan2 _0 (negate _1)--- 3.141592653589793------ >>> atan2 (negate _1) _0--- -1.5707963267948966-atan2 :: (RealFloat a) => Quantity d a -> Quantity d a -> Dimensionless a-atan2 = liftQ2 Prelude.atan2--{--The only unit we will define in this module is 'one'.--}---- | The unit 'one' has dimension 'DOne' and is the base unit of dimensionless values.------ As detailed in 7.10 "Values of quantities expressed simply as numbers:--- the unit one, symbol 1" of <#note1 [1]> the unit one generally does not--- appear in expressions. However, for us it is necessary to use 'one'--- as we would any other unit to perform the "boxing" of dimensionless values.-one :: Num a => Unit 'NonMetric DOne a-one = Unit nOne 1 1--{- $constants-For convenience we define some constants for small integer values-that often show up in formulae. We also throw in 'pi' and 'tau' for-good measure.---}---- | The constant for zero is polymorphic, allowing it to express zero 'Length' or--- 'Numeric.Units.Dimensional.Quantities.Capacitance' or 'Numeric.Units.Dimensional.Quantities.Velocity' etc,--- in addition to the 'Dimensionless' value zero.-_0 :: Num a => Quantity d a-_0 = Quantity 0--_1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Num a) => Dimensionless a-_1 = 1 *~ one-_2 = 2 *~ one-_3 = 3 *~ one-_4 = 4 *~ one-_5 = 5 *~ one-_6 = 6 *~ one-_7 = 7 *~ one-_8 = 8 *~ one-_9 = 9 *~ one--pi :: Floating a => Dimensionless a-pi = Prelude.pi *~ one---- | Twice 'pi'.------ For background on 'tau' see http://tauday.com/tau-manifesto (but also--- feel free to review http://www.thepimanifesto.com).-tau :: Floating a => Dimensionless a-tau = _2 * pi--{- $functor-We intentionally decline to provide a 'Functor' instance for 'Dimensional' because its use breaks the-abstraction of physical dimensions.--If you feel your work requires this instance, it is provided as an orphan in "Numeric.Units.Dimensional.Functor".---}---- | Convenient conversion between numerical types while retaining dimensional information.------ >>> let x = (37 :: Rational) *~ poundMass--- >>> changeRep x :: Mass Double--- 16.78291769 kg-changeRep :: (KnownVariant v, Real a, Fractional b) => Dimensional v d a -> Dimensional v d b-changeRep = dmap realToFrac---- | Convenient conversion from exactly represented values while retaining dimensional information.-changeRepApproximate :: (KnownVariant v, Floating b) => Dimensional v d ExactPi -> Dimensional v d b-changeRepApproximate = dmap approximateValue--{- $lenses-These functions are compatible with the lens library.---}---- | Converts a 'Unit' into a lens from 'Quantity's to values.-asLens :: (Fractional a) => Unit m d a- -> (forall f.Functor f => (a -> f a)- -> Quantity d a- -> f (Quantity d a))-asLens u f q = fmap (\v' -> v' *~ u) (f (q /~ u))--{- $dimension-terms-To facilitate parsing and pretty-printing functions that may wish to operate on term-level representations of dimension,-we provide a means for converting from type-level dimensions to term-level dimensions.---}---- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'.------ Use this variant when the scale factor of the resulting unit is irrational or 'Approximate'. See 'mkUnitQ' for when it is rational--- and 'mkUnitZ' for when it is an integer.------ Note that supplying zero as a definining quantity is invalid, as the library relies--- upon units forming a group under multiplication.------ Supplying negative defining quantities is allowed and handled gracefully, but is discouraged--- on the grounds that it may be unexpected by other readers.-mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a-mkUnitR n s (Unit _ e _) | isExactZero s = error "Supplying zero as a conversion factor is not valid."- | otherwise = Unit n e' x'- where- e' = s Prelude.* e- x' = approximateValue e'---- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'.------ Use this variant when the scale factor of the resulting unit is rational. See 'mkUnitZ' for when it is an integer--- and 'mkUnitR' for the general case.------ For more information see 'mkUnitR'.-mkUnitQ :: Fractional a => UnitName m -> Rational -> Unit m1 d a -> Unit m d a-mkUnitQ n s (Unit _ e x) | s == 0 = error "Supplying zero as a conversion factor is not valid."- | Just x'' <- toExactRational e' = Unit n e' (fromRational x'')- | otherwise = Unit n e' x'- where- e' = fromRational s Prelude.* e- x' = fromRational s Prelude.* x---- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'.------ Use this variant when the scale factor of the resulting unit is an integer. See 'mkUnitQ' for when it is rational--- and 'mkUnitR' for the general case.------ For more information see 'mkUnitR'.-mkUnitZ :: Num a => UnitName m -> Integer -> Unit m1 d a -> Unit m d a-mkUnitZ n s (Unit _ e x) | s == 0 = error "Supplying zero as a conversion factor is not valid."- | Just x'' <- toExactInteger e' = Unit n e' (fromInteger x'')- | otherwise = Unit n e' x'- where- e' = fromInteger s Prelude.* e- x' = fromInteger s Prelude.* x+{-# OPTIONS_HADDOCK show-extensions #-} + +{-# LANGUAGE AutoDeriveTypeable #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + += Summary + +In this module we provide data types for performing arithmetic with +physical quantities and units. Information about the physical +dimensions of the quantities/units is embedded in their types and +the validity of operations is verified by the type checker at compile +time. The boxing and unboxing of numerical values as quantities is +done by multiplication and division of units, of which an incomplete +set is provided. + +We limit ourselves to \"Newtonian\" physics. We do not attempt to +accommodate relativistic physics in which e.g. addition of length +and time would be valid. + +As far as possible and/or practical the conventions and guidelines +of NIST's "Guide for the Use of the International System of Units +(SI)" <#note1 [1]> are followed. Occasionally we will reference specific +sections from the guide and deviations will be explained. + +== Disclaimer + +Merely an engineer, the author doubtlessly uses a language and +notation that makes mathematicians and physicist cringe. He does +not mind constructive criticism (or pull requests). + +The sets of functions and units defined herein are incomplete and +reflect only the author's needs to date. Again, patches are welcome. + += Usage + +== Preliminaries + +This module requires GHC 7.8 or later. We utilize Data Kinds, TypeNats, +Closed Type Families, etc. Clients of the module are generally not +required to use these extensions. + +Clients probably will want to use the NegativeLiterals extension. + +== Examples + +We have defined operators and units that allow us to define and +work with physical quantities. A physical quantity is defined by +multiplying a number with a unit (the type signature is optional). + +> v :: Velocity Prelude.Double +> v = 90 *~ (kilo meter / hour) + +It follows naturally that the numerical value of a quantity is +obtained by division by a unit. + +> numval :: Prelude.Double +> numval = v /~ (meter / second) + +The notion of a quantity as the product of a numerical value and a +unit is supported by 7.1 "Value and numerical value of a quantity" of +<#note1 [1]>. While the above syntax is fairly natural it is unfortunate that +it must violate a number of the guidelines in <#note1 [1]>, in particular 9.3 +"Spelling unit names with prefixes", 9.4 "Spelling unit names obtained +by multiplication", 9.5 "Spelling unit names obtained by division". + +As a more elaborate example of how to use the module we define a +function for calculating the escape velocity of a celestial body +<#note2 [2]>. + +> escapeVelocity :: (Floating a) => Mass a -> Length a -> Velocity a +> escapeVelocity m r = sqrt (two * g * m / r) +> where +> two = 2 *~ one +> g = 6.6720e-11 *~ (newton * meter ^ pos2 / kilo gram ^ pos2) + +For completeness we should also show an example of the error messages +we will get from GHC when performing invalid arithmetic. In the +best case GHC will be able to use the type synonyms we have defined +in its error messages. + +> let x = 1 *~ meter + 1 *~ second +> +> Couldn't match type 'Numeric.NumType.DK.Integers.Zero +> with 'Numeric.NumType.DK.Integers.Pos1 +> Expected type: Unit 'Metric DLength a +> Actual type: Unit 'Metric DTime a +> In the second argument of `(*~)', namely `second' +> In the second argument of `(+)', namely `1 *~ second' + +In other cases the error messages aren't very friendly. + +> let x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram +> +> Couldn't match type 'Numeric.NumType.DK.Integers.Zero +> with 'Numeric.NumType.DK.Integers.Neg1 +> Expected type: Quantity DMass a +> Actual type: Dimensional +> ('DQuantity V.* 'DQuantity) (DLength / DTime) a +> In the first argument of `(+)', namely `1 *~ meter / (1 *~ second)' +> In the expression: 1 *~ meter / (1 *~ second) + 1 *~ kilo gram +> In an equation for `x': +> x = 1 *~ meter / (1 *~ second) + 1 *~ kilo gram + +It is the author's experience that the usefulness of the compiler +error messages is more often than not limited to pinpointing the +location of errors. + += Notes + +== Future work + +While there is an insane amount of units in use around the world +it is reasonable to provide those in relatively widespread use. Units outside +of SI will most likely be added on an as-needed basis. + +Additional physics models could be implemented. See <#note3 [3]> for ideas. + +== Related work + +Henning Thielemann numeric prelude has a physical units library, +however, checking of dimensions is dynamic rather than static. +Aaron Denney has created a toy example of statically checked +physical dimensions covering only length and time. HaskellWiki +has pointers <#note4 [4]> to these. + +Also see Samuel Hoffstaetter's blog post <#note5 [5]> which uses techniques +similar to this library. + +Libraries with similar functionality exist for other programming +languages and may serve as inspiration. The author has found the +Java library JScience <#note6 [6]> and the Fortress programming language <#note7 [7]> +particularly noteworthy. + +== References + +1. #note1# http://physics.nist.gov/Pubs/SP811/ +2. #note2# http://en.wikipedia.org/wiki/Escape_velocity +3. #note3# http://jscience.org/api/org/jscience/physics/models/package-summary.html +4. #note4# http://www.haskell.org/haskellwiki/Physical_units +5. #note5# http://liftm.wordpress.com/2007/06/03/scientificdimension-type-arithmetic-and-physical-units-in-haskell/ +6. #note6# http://jscience.org/ +7. #note7# http://research.sun.com/projects/plrg/fortress.pdf + +-} + +module Numeric.Units.Dimensional + ( + -- * Types + -- $types + Dimensional, + Unit, Quantity, + Metricality(..), + -- * Physical Dimensions + -- $dimensions + Dimension (Dim), + -- ** Dimension Arithmetic + -- $dimension-arithmetic + type (*), type (/), type (^), NRoot, Sqrt, Cbrt, Recip, + -- ** Term Level Representation of Dimensions + -- $dimension-terms + Dimension' (Dim'), HasDimension(..), KnownDimension, + -- * Dimensional Arithmetic + (*~), (/~), + (^), (^/), (**), (*), (/), (+), (-), + negate, abs, signum, recip, nroot, sqrt, cbrt, + -- ** Transcendental Functions + exp, log, logBase, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, atan2, + -- ** Operations on Collections + -- $collections + (*~~), (/~~), sum, mean, product, dimensionlessLength, nFromTo, + -- * Dimension Synonyms + -- $dimension-synonyms + DOne, DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity, + -- * Quantity Synonyms + -- $quantity-synonyms + Dimensionless, Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity, + -- * Constants + -- $constants + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau, + -- * Constructing Units + siUnit, one, mkUnitR, mkUnitQ, mkUnitZ, + -- * Unit Metadata + name, exactValue, weaken, strengthen, exactify, + -- * Pretty Printing + showIn, + -- * On 'Functor', and Conversion Between Number Representations + -- $functor + KnownVariant(dmap), changeRep, changeRepApproximate, + -- * Lenses + -- $lenses + asLens + ) + where + +import Prelude + ( Eq(..), Num, Fractional, Floating, Real, RealFloat, Functor, fmap + , (.), flip, (++), fromIntegral, fromInteger, fromRational, error, max, succ + , Int, Integer, Integral, ($), uncurry, realToFrac, otherwise + ) +import qualified Prelude +import Numeric.NumType.DK.Integers + ( pos2, pos3 + , KnownTypeInt, toNum + ) +import Data.Data +import Data.ExactPi +import Data.Foldable (Foldable(foldr)) +import Data.Maybe +import Data.Ratio +import Numeric.Units.Dimensional.Dimensions +import Numeric.Units.Dimensional.Internal +import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen, product) +import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name +import Numeric.Units.Dimensional.Variants hiding (type (*), type (/)) +import qualified Numeric.Units.Dimensional.Variants as V + +-- Provide a version of length which is compatible with base-4.8's version. +-- Where 4.8 is available we use that version as it may have performance advantages. +-- Where it is not available we implement it in terms of foldl'. +#if MIN_VERSION_base(4,8,0) +import Data.Foldable (Foldable(length)) +#else +import Data.Foldable (Foldable(foldl')) + +length :: Foldable t => t a -> Int +length = foldl' (\c _ -> c Prelude.+ 1) 0 +#endif + +-- $setup +-- >>> :set -XFlexibleInstances +-- >>> :set -XNoImplicitPrelude +-- >>> import Test.QuickCheck.Arbitrary +-- >>> import Numeric.Units.Dimensional.Prelude +-- >>> import Numeric.Units.Dimensional.Float +-- >>> import Numeric.Units.Dimensional.NonSI +-- >>> instance Arbitrary a => Arbitrary (Quantity d a) where arbitrary = fmap Quantity arbitrary + +{- +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. +-} + +infixr 8 ^, ^/, ** +infixl 7 *, / +infixl 6 +, - + + +{- $types +Our primary objective is to define a data type that can be used to +represent (while still differentiating between) units and quantities. +There are two reasons for consolidating units and quantities in one +data type. The first being to allow code reuse as they are largely +subject to the same operations. The second being that it allows +reuse of operators (and functions) between the two without resorting +to occasionally cumbersome type classes. + +The relationship between (the value of) a 'Quantity', its numerical +value and its 'Unit' is described in 7.1 "Value and numerical value +of a quantity" of <#note1 [1]>. In short a 'Quantity' is the product of a +number and a 'Unit'. We define the '*~' operator as a convenient +way to declare quantities as such a product. + +-} + +-- | Extracts the 'UnitName' of a 'Unit'. +name :: Unit m d a -> UnitName m +name (Unit n _ _) = n + +-- | Extracts the exact value of a 'Unit', expressed in terms of the SI coherent derived unit (see 'siUnit') of the same 'Dimension'. +-- +-- Note that the actual value may in some cases be approximate, for example if the unit is defined by experiment. +exactValue :: Unit m d a -> ExactPi +exactValue (Unit _ e _) = e + +-- | Discards potentially unwanted type level information about a 'Unit'. +weaken :: Unit m d a -> Unit 'NonMetric d a +weaken (Unit n e v) = Unit (Name.weaken n) e v + +-- | Attempts to convert a 'Unit' which may or may not be 'Metric' to one +-- which is certainly 'Metric'. +strengthen :: Unit m d a -> Maybe (Unit 'Metric d a) +strengthen (Unit n e v) | Just n' <- Name.strengthen n = Just $ Unit n' e v + | otherwise = Nothing + +-- | Forms the exact version of a 'Unit'. +exactify :: Unit m d a -> Unit m d ExactPi +exactify (Unit n e _) = Unit n e e + +-- | Forms a 'Quantity' by multipliying a number and a unit. +(*~) :: (Num a) => a -> Unit m d a -> Quantity d a +x *~ (Unit _ _ y) = Quantity (x Prelude.* y) + +-- | Divides a 'Quantity' by a 'Unit' of the same physical dimension, obtaining the +-- numerical value of the quantity expressed in that unit. +(/~) :: Fractional a => Quantity d a -> Unit m d a -> a +(Quantity x) /~ (Unit _ _ y) = (x Prelude./ y) + +{- +We give '*~' and '/~' the same fixity as '*' and '/' defined below. +Note that this necessitates the use of parenthesis when composing +units using '*' and '/', e.g. "1 *~ (meter / second)". +-} + +infixl 7 *~, /~ + +{- $dimensions +The phantom type variable d encompasses the physical dimension of +a 'Dimensional'. As detailed in <#note5 [5]> there are seven base dimensions, +which can be combined in integer powers to a given physical dimension. +We represent physical dimensions as the powers of the seven base +dimensions that make up the given dimension. The powers are represented +using NumTypes. For convenience we collect all seven base dimensions +in a data kind 'Dimension'. + +We could have chosen to provide type variables for the seven base +dimensions in 'Dimensional' instead of creating a new data kind +'Dimension'. However, that would have made any type signatures involving +'Dimensional' very cumbersome. By encompassing the physical dimension +in a single type variable we can "hide" the cumbersome type arithmetic +behind convenient type classes as will be seen later. + +-} + +{- $dimension-synonyms +Using our 'Dimension' data kind we define some type synonyms for convenience. +We start with the base dimensions, others can be found in "Numeric.Units.Dimensional.Quantities". + +-} + +{- $quantity-synonyms +Using the above type synonyms we can define type synonyms for +quantities of particular physical dimensions. + +Again we limit ourselves to the base dimensions, others can be found in "Numeric.Units.Dimensional.Quantities". + +-} + +type Dimensionless = Quantity DOne +type Length = Quantity DLength +type Mass = Quantity DMass +type Time = Quantity DTime +type ElectricCurrent = Quantity DElectricCurrent +type ThermodynamicTemperature = Quantity DThermodynamicTemperature +type AmountOfSubstance = Quantity DAmountOfSubstance +type LuminousIntensity = Quantity DLuminousIntensity + +{- $dimension-arithmetic +When performing arithmetic on units and quantities the arithmetics +must be applied to both the numerical values of the Dimensionals +but also to their physical dimensions. The type level arithmetic +on physical dimensions is governed by closed type families expressed +as type operators. + +We could provide the 'Mul' and 'Div' classes with full functional +dependencies but that would be of limited utility as there is no +limited use for "backwards" type inference. Efforts are underway to +develop a type-checker plugin that does enable these scenarios, e.g. +for linear algebra. + +-} + +{- += Arithmetic on units and quantities = + +Thanks to the arithmetic on physical dimensions having been sorted +out separately a lot of the arithmetic on Dimensionals is straight +forward. In particular the type signatures are much simplified. + +Multiplication, division and powers apply to both units and quantities. +-} + +-- | Multiplies two 'Quantity's or two 'Unit's. +-- +-- The intimidating type signature captures the similarity between these operations +-- and ensures that composite 'Unit's are 'NonMetric'. +(*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V.* v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V.* v2) (d1 * d2) a +(*) = liftD2 (Prelude.*) (Prelude.*) (Name.*) + +-- | Divides one 'Quantity' by another or one 'Unit' by another. +-- +-- The intimidating type signature captures the similarity between these operations +-- and ensures that composite 'Unit's are 'NotPrefixable'. +(/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 V./ v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 V./ v2) (d1 / d2) a +(/) = liftD2 (Prelude./) (Prelude./) (Name./) + +-- | Forms the reciprocal of a 'Quantity', which has the reciprocal dimension. +-- +-- >>> recip $ 47 *~ hertz +-- 2.127659574468085e-2 s +recip :: (Fractional a) => Quantity d a -> Quantity (Recip d) a +recip = liftQ Prelude.recip + +-- | Raises a 'Quantity' or 'Unit' to an integer power. +-- +-- Because the power chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation +-- of the exponent in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ... +-- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are +-- also reexported by "Numeric.Units.Dimensional.Prelude". +-- +-- The intimidating type signature captures the similarity between these operations +-- and ensures that composite 'Unit's are 'NotPrefixable'. +(^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v)) + => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a +x ^ n = let n' = (toNum n) :: Int + in liftD (Prelude.^^ n') (Prelude.^^ n') (Name.^ n') x + +{- +A special case is that dimensionless quantities are not restricted +to integer exponents. This is accommodated by the '**' operator +defined later. + + += Quantity operations = + +Some additional operations obviously only make sense for quantities. +Of these, negation, addition and subtraction are particularly simple +as they are done in a single physical dimension. +-} + +-- | Negates the value of a 'Quantity'. +negate :: Num a => Quantity d a -> Quantity d a +negate = liftQ Prelude.negate + +-- | Adds two 'Quantity's. +(+) :: Num a => Quantity d a -> Quantity d a -> Quantity d a +(+) = liftQ2 (Prelude.+) + +-- | Subtracts one 'Quantity' from another. +(-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a +(-) = liftQ2 (Prelude.-) + +-- | Takes the absolute value of a 'Quantity'. +abs :: Num a => Quantity d a -> Quantity d a +abs = liftQ Prelude.abs + +-- | Takes the sign of a 'Quantity'. The functions 'abs' and 'signum' +-- satisy the law that: +-- +-- > abs x * signum x == x +-- +-- The sign is either @negate _1@ (negative), @_0@ (zero), +-- or @_1@ (positive). +signum :: Num a => Quantity d a -> Dimensionless a +signum = liftQ Prelude.signum + +{- +Roots of arbitrary (integral) degree. Appears to occasionally be useful +for units as well as quantities. +-} + +-- | Computes the nth root of a 'Quantity' using 'Prelude.**'. +-- +-- The 'NRoot' type family will prevent application of this operator where the result would have a fractional dimension or where n is zero. +-- +-- Because the root chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation +-- of the root in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ... +-- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are +-- also reexported by "Numeric.Units.Dimensional.Prelude". +-- +-- n must not be zero. Negative roots are defined such that @nroot (Proxy :: Proxy (Negate n)) x == nroot (Proxy :: Proxy n) (recip x)@. +-- +-- Also available in operator form, see '^/'. +nroot :: (KnownTypeInt n, Floating a) + => Proxy n -> Quantity d a -> Quantity (NRoot d n) a +nroot n = let n' = 1 Prelude./ toNum n + in liftQ (Prelude.** n') + +{- +We provide short-hands for the square and cube roots. +-} + +-- | Computes the square root of a 'Quantity' using 'Prelude.**'. +-- +-- The 'NRoot' type family will prevent application where the supplied quantity does not have a square dimension. +-- +-- prop> (x :: Area Double) >= _0 ==> sqrt x == nroot pos2 x +sqrt :: Floating a => Quantity d a -> Quantity (Sqrt d) a +sqrt = nroot pos2 + +-- | Computes the cube root of a 'Quantity' using 'Prelude.**'. +-- +-- The 'NRoot' type family will prevent application where the supplied quantity does not have a cubic dimension. +-- +-- prop> (x :: Volume Double) >= _0 ==> cbrt x == nroot pos3 x +cbrt :: Floating a => Quantity d a -> Quantity (Cbrt d) a +cbrt = nroot pos3 + +{- +We also provide an operator alternative to nroot for those that +prefer such. +-} + +-- | Computes the nth root of a 'Quantity' using 'Prelude.**'. +-- +-- The 'NRoot' type family will prevent application of this operator where the result would have a fractional dimension or where n is zero. +-- +-- Because the root chosen impacts the 'Dimension' of the result, it is necessary to supply a type-level representation +-- of the root in the form of a 'Proxy' to some 'TypeInt'. Convenience values 'pos1', 'pos2', 'neg1', ... +-- are supplied by the "Numeric.NumType.DK.Integers" module. The most commonly used ones are +-- also reexported by "Numeric.Units.Dimensional.Prelude". +-- +-- Also available in prefix form, see 'nroot'. +(^/) :: (KnownTypeInt n, Floating a) + => Quantity d a -> Proxy n -> Quantity (NRoot d n) a +(^/) = flip nroot + +{- $collections +Here we define operators and functions to make working with homogenuous +lists of dimensionals more convenient. + +We define two convenience operators for applying units to all +elements of a functor (e.g. a list). +-} + +-- | Applies '*~' to all values in a functor. +(*~~) :: (Functor f, Num a) => f a -> Unit m d a -> f (Quantity d a) +xs *~~ u = fmap (*~ u) xs + +-- | Applies '/~' to all values in a functor. +(/~~) :: forall f m d a.(Functor f, Fractional a) => f (Quantity d a) -> Unit m d a -> f a +xs /~~ u = fmap (/~ u) xs + +infixl 7 *~~, /~~ + +-- | The sum of all elements in a foldable structure. +-- +-- >>> sum ([] :: [Mass Double]) +-- 0.0 kg +-- +-- >>> sum [12.4 *~ meter, 1 *~ foot] +-- 12.7048 m +sum :: (Num a, Foldable f) => f (Quantity d a) -> Quantity d a +sum = foldr (+) _0 + +-- | The product of all elements in a foldable structure. +-- +-- >>> product ([] :: [Dimensionless Double]) +-- 1.0 +-- +-- >>> product [pi, _4, 0.36 *~ one] +-- 4.523893421169302 +product :: (Num a, Foldable f) => f (Dimensionless a) -> Dimensionless a +product = foldr (*) _1 + +-- | The arithmetic mean of all elements in a foldable structure. +-- +-- >>> mean [pi, _7] +-- 5.070796326794897 +mean :: (Fractional a, Foldable f) => f (Quantity d a) -> Quantity d a +mean = uncurry (/) . foldr accumulate (_0, _0) + where + accumulate val (accum, count) = (accum + val, count + _1) + +-- | The length of the foldable data structure as a 'Dimensionless'. +-- This can be useful for purposes of e.g. calculating averages. +-- +-- >>> dimensionlessLength ["foo", "bar"] +-- 2 +dimensionlessLength :: (Num a, Foldable f) => f b -> Dimensionless a +dimensionlessLength x = (fromIntegral $ length x) *~ one + +-- | Returns a list of quantities between given bounds. +-- +-- prop> n <= 0 ==> nFromTo (x :: Mass Double) (y :: Mass Double) n == [x, y] +-- +-- prop> (x :: Length Double) <= (y :: Length Double) ==> all (\z -> x <= z && z <= y) (nFromTo x y n) +-- +-- >>> nFromTo _0 _3 2 +-- [0.0,1.0,2.0,3.0] +-- +-- >>> nFromTo _1 _0 7 +-- [1.0,0.875,0.75,0.625,0.5,0.375,0.25,0.125,0.0] +-- +-- >>> nFromTo _0 _1 (-5) +-- [0.0,1.0] +nFromTo :: (Fractional a, Integral b) => Quantity d a -- ^ The initial value. + -> Quantity d a -- ^ The final value. + -> b -- ^ The number of intermediate values. If less than one, no intermediate values will result. + -> [Quantity d a] +nFromTo xi xf n = fmap f [0..n'] ++ [xf] + where + n' = max 0 n + f i = xi + realToFrac (i % succ n') *~ one * (xf - xi) + +{- +We continue by defining elementary functions on 'Dimensionless' +that may be obviously useful. +-} + +exp, log, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh + :: Floating a => Dimensionless a -> Dimensionless a +exp = fmap Prelude.exp +log = fmap Prelude.log +sin = fmap Prelude.sin +cos = fmap Prelude.cos +tan = fmap Prelude.tan +asin = fmap Prelude.asin +acos = fmap Prelude.acos +atan = fmap Prelude.atan +sinh = fmap Prelude.sinh +cosh = fmap Prelude.cosh +tanh = fmap Prelude.tanh +asinh = fmap Prelude.asinh +acosh = fmap Prelude.acosh +atanh = fmap Prelude.atanh + +-- | Raises a dimensionless quantity to a dimensionless power. +(**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a +(**) = liftQ2 (Prelude.**) + +-- | Takes the logarithm of the second argument in the base of the first. +-- +-- >>> logBase _2 _8 +-- 3.0 +logBase :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a +logBase = liftQ2 Prelude.logBase + +-- | The standard two argument arctangent function. +-- Since it interprets its two arguments in comparison with one another, the input may have any dimension. +-- +-- >>> atan2 _0 _1 +-- 0.0 +-- +-- >>> atan2 _1 _0 +-- 1.5707963267948966 +-- +-- >>> atan2 _0 (negate _1) +-- 3.141592653589793 +-- +-- >>> atan2 (negate _1) _0 +-- -1.5707963267948966 +atan2 :: (RealFloat a) => Quantity d a -> Quantity d a -> Dimensionless a +atan2 = liftQ2 Prelude.atan2 + +{- +The only unit we will define in this module is 'one'. +-} + +-- | The unit 'one' has dimension 'DOne' and is the base unit of dimensionless values. +-- +-- As detailed in 7.10 "Values of quantities expressed simply as numbers: +-- the unit one, symbol 1" of <#note1 [1]> the unit one generally does not +-- appear in expressions. However, for us it is necessary to use 'one' +-- as we would any other unit to perform the "boxing" of dimensionless values. +one :: Num a => Unit 'NonMetric DOne a +one = Unit nOne 1 1 + +{- $constants +For convenience we define some constants for small integer values +that often show up in formulae. We also throw in 'pi' and 'tau' for +good measure. + +-} + +-- | The constant for zero is polymorphic, allowing it to express zero 'Length' or +-- 'Numeric.Units.Dimensional.Quantities.Capacitance' or 'Numeric.Units.Dimensional.Quantities.Velocity' etc, +-- in addition to the 'Dimensionless' value zero. +_0 :: Num a => Quantity d a +_0 = Quantity 0 + +_1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Num a) => Dimensionless a +_1 = 1 *~ one +_2 = 2 *~ one +_3 = 3 *~ one +_4 = 4 *~ one +_5 = 5 *~ one +_6 = 6 *~ one +_7 = 7 *~ one +_8 = 8 *~ one +_9 = 9 *~ one + +pi :: Floating a => Dimensionless a +pi = Prelude.pi *~ one + +-- | Twice 'pi'. +-- +-- For background on 'tau' see http://tauday.com/tau-manifesto (but also +-- feel free to review http://www.thepimanifesto.com). +tau :: Floating a => Dimensionless a +tau = _2 * pi + +{- $functor +We intentionally decline to provide a 'Functor' instance for 'Dimensional' because its use breaks the +abstraction of physical dimensions. + +If you feel your work requires this instance, it is provided as an orphan in "Numeric.Units.Dimensional.Functor". + +-} + +-- | Convenient conversion between numerical types while retaining dimensional information. +-- +-- >>> let x = (37 :: Rational) *~ poundMass +-- >>> changeRep x :: Mass Double +-- 16.78291769 kg +changeRep :: (KnownVariant v, Real a, Fractional b) => Dimensional v d a -> Dimensional v d b +changeRep = dmap realToFrac + +-- | Convenient conversion from exactly represented values while retaining dimensional information. +changeRepApproximate :: (KnownVariant v, Floating b) => Dimensional v d ExactPi -> Dimensional v d b +changeRepApproximate = dmap approximateValue + +{- $lenses +These functions are compatible with the lens library. + +-} + +-- | Converts a 'Unit' into a lens from 'Quantity's to values. +asLens :: (Fractional a) => Unit m d a + -> (forall f.Functor f => (a -> f a) + -> Quantity d a + -> f (Quantity d a)) +asLens u f q = fmap (\v' -> v' *~ u) (f (q /~ u)) + +{- $dimension-terms +To facilitate parsing and pretty-printing functions that may wish to operate on term-level representations of dimension, +we provide a means for converting from type-level dimensions to term-level dimensions. + +-} + +-- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'. +-- +-- Use this variant when the scale factor of the resulting unit is irrational or 'Approximate'. See 'mkUnitQ' for when it is rational +-- and 'mkUnitZ' for when it is an integer. +-- +-- Note that supplying zero as a definining quantity is invalid, as the library relies +-- upon units forming a group under multiplication. +-- +-- Supplying negative defining quantities is allowed and handled gracefully, but is discouraged +-- on the grounds that it may be unexpected by other readers. +mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a +mkUnitR n s (Unit _ e _) | isExactZero s = error "Supplying zero as a conversion factor is not valid." + | otherwise = Unit n e' x' + where + e' = s Prelude.* e + x' = approximateValue e' + +-- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'. +-- +-- Use this variant when the scale factor of the resulting unit is rational. See 'mkUnitZ' for when it is an integer +-- and 'mkUnitR' for the general case. +-- +-- For more information see 'mkUnitR'. +mkUnitQ :: Fractional a => UnitName m -> Rational -> Unit m1 d a -> Unit m d a +mkUnitQ n s (Unit _ e x) | s == 0 = error "Supplying zero as a conversion factor is not valid." + | Just x'' <- toExactRational e' = Unit n e' (fromRational x'') + | otherwise = Unit n e' x' + where + e' = fromRational s Prelude.* e + x' = fromRational s Prelude.* x + +-- | Forms a new atomic 'Unit' by specifying its 'UnitName' and its definition as a multiple of another 'Unit'. +-- +-- Use this variant when the scale factor of the resulting unit is an integer. See 'mkUnitQ' for when it is rational +-- and 'mkUnitR' for the general case. +-- +-- For more information see 'mkUnitR'. +mkUnitZ :: Num a => UnitName m -> Integer -> Unit m1 d a -> Unit m d a +mkUnitZ n s (Unit _ e x) | s == 0 = error "Supplying zero as a conversion factor is not valid." + | Just x'' <- toExactInteger e' = Unit n e' (fromInteger x'') + | otherwise = Unit n e' x' + where + e' = fromInteger s Prelude.* e + x' = fromInteger s Prelude.* x
src/Numeric/Units/Dimensional/Coercion.hs view
@@ -1,35 +1,35 @@-{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Experimental- Portability: GHC only?--Re-exports the raw 'Quantity' constructor from the Numeric.Units.Dimensional.Internal module, along with 'Data.Coerce.coerce',-for convenience in converting between raw representations and dimensional values.--Note that use of these constructs requires the user to verify the dimensional safety of the conversion,-because the coercion doesn't explicitly mention the unit of the representation. Note also that the-'Quantity' constructor constructs a 'Numeric.Units.Dimensional.SQuantity' which may have a scale factor-other than 'Data.ExactPi.TypeLevel.One'.--Note that the haddock documentation doesn't mention the 'Quantity' constructor because it is a part of the-'Dimensional' associated data family, but it is exported by this module.---}--module Numeric.Units.Dimensional.Coercion-(- coerce, Dimensional(Quantity), unQuantity-)-where--import Data.Coerce (coerce)-import Numeric.Units.Dimensional.Internal (SQuantity, Dimensional(Quantity))---- | Unwraps a possibly-scaled `SQuantity`, yielding its underlying representation.------ This is a type-restricted version of `coerce`.-unQuantity :: SQuantity s d a -> a-unQuantity = coerce+{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Experimental + Portability: GHC only? + +Re-exports the raw 'Quantity' constructor from the Numeric.Units.Dimensional.Internal module, along with 'Data.Coerce.coerce', +for convenience in converting between raw representations and dimensional values. + +Note that use of these constructs requires the user to verify the dimensional safety of the conversion, +because the coercion doesn't explicitly mention the unit of the representation. Note also that the +'Quantity' constructor constructs a 'Numeric.Units.Dimensional.SQuantity' which may have a scale factor +other than 'Data.ExactPi.TypeLevel.One'. + +Note that the haddock documentation doesn't mention the 'Quantity' constructor because it is a part of the +'Dimensional' associated data family, but it is exported by this module. + +-} + +module Numeric.Units.Dimensional.Coercion +( + coerce, Dimensional(Quantity), unQuantity +) +where + +import Data.Coerce (coerce) +import Numeric.Units.Dimensional.Internal (SQuantity, Dimensional(Quantity)) + +-- | Unwraps a possibly-scaled `SQuantity`, yielding its underlying representation. +-- +-- This is a type-restricted version of `coerce`. +unQuantity :: SQuantity s d a -> a +unQuantity = coerce
src/Numeric/Units/Dimensional/Dimensions.hs view
@@ -1,25 +1,25 @@-{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--Provides both term-level and type-level representations for physical dimensions in-a single import for convenience.--Presuming that users intend to work primarily with type level dimensions, this module hides-arithmetic operators over term level dimensions and aliases for the base term-level dimensions-to avoid namespace pollution. These features are available directly from-"Numeric.Units.Dimensional.Dimensions.TermLevel" if desired.--}-module Numeric.Units.Dimensional.Dimensions-(- module Numeric.Units.Dimensional.Dimensions.TermLevel,- module Numeric.Units.Dimensional.Dimensions.TypeLevel-)-where--import Numeric.Units.Dimensional.Dimensions.TermLevel hiding ((*), (/), (^), recip, nroot, sqrt, cbrt, dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity)-import Numeric.Units.Dimensional.Dimensions.TypeLevel+{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +Provides both term-level and type-level representations for physical dimensions in +a single import for convenience. + +Presuming that users intend to work primarily with type level dimensions, this module hides +arithmetic operators over term level dimensions and aliases for the base term-level dimensions +to avoid namespace pollution. These features are available directly from +"Numeric.Units.Dimensional.Dimensions.TermLevel" if desired. +-} +module Numeric.Units.Dimensional.Dimensions +( + module Numeric.Units.Dimensional.Dimensions.TermLevel, + module Numeric.Units.Dimensional.Dimensions.TypeLevel +) +where + +import Numeric.Units.Dimensional.Dimensions.TermLevel hiding ((*), (/), (^), recip, nroot, sqrt, cbrt, dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity) +import Numeric.Units.Dimensional.Dimensions.TypeLevel
src/Numeric/Units/Dimensional/Dimensions/TermLevel.hs view
@@ -1,192 +1,192 @@-{-# OPTIONS_HADDOCK not-home, show-extensions #-}--{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--This module defines physical dimensions expressed in terms of-the SI base dimensions, including arithmetic.---}-module Numeric.Units.Dimensional.Dimensions.TermLevel-(- -- * Type- Dimension'(..),- -- * Access to Dimension of Dimensional Values- HasDimension(..), HasDynamicDimension(..), DynamicDimension(..),- -- * Dimension Arithmetic- (*), (/), (^), recip, nroot, sqrt, cbrt,- -- * Synonyms for Base Dimensions- dOne,- dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity,- -- * Deconstruction- asList,- -- * Examining Dynamic Dimensions- matchDimensions, isCompatibleWith, hasSomeDimension-)-where--import Control.DeepSeq-import Data.Data-import Data.Semigroup (Semigroup(..))-import Data.Monoid (Monoid(..))-import GHC.Generics-import Prelude (id, all, fst, snd, fmap, otherwise, divMod, ($), (+), (-), (.), (&&), Int, Show, Eq(..), Ord(..), Maybe(..), Bool(..))-import qualified Prelude as P---- $setup--- >>> import Prelude (negate)--- >>> import Control.Applicative--- >>> import Test.QuickCheck.Arbitrary--- >>> instance Arbitrary Dimension' where arbitrary = Dim' <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary---- | A physical dimension, encoded as 7 integers, representing a factorization of the dimension into the--- 7 SI base dimensions. By convention they are stored in the same order as--- in the 'Numeric.Units.Dimensional.Dimensions.TypeLevel.Dimension' data kind.-data Dimension' = Dim' !Int !Int !Int !Int !Int !Int !Int- deriving (Show, Eq, Ord, Data, Generic, Typeable)--instance NFData Dimension' where- rnf !_ = () -- The Dimension' constructor is already fully strict.--instance Semigroup Dimension' where- (<>) = (*)---- | The monoid of dimensions under multiplication.-instance Monoid Dimension' where- mempty = dOne- mappend = (Data.Semigroup.<>)---- | The dimension of a dynamic value, which may not have any dimension at all.-data DynamicDimension = NoDimension -- ^ The value has no valid dimension.- | SomeDimension Dimension' -- ^ The value has the given dimension.- | AnyDimension -- ^ The value may be interpreted as having any dimension.- deriving (Eq, Ord, Show, Data, Generic, Typeable)--instance NFData DynamicDimension where---- | Dimensional values, or those that are only possibly dimensional, inhabit this class,--- which allows access to a term-level representation of their dimension.-class HasDynamicDimension a where- -- | Gets the 'DynamicDimension of a dynamic dimensional value, which may be 'NoDimension' if it does not represent- -- a dimensional value of any 'Dimension'.- --- -- A default implementation is available for types that are also in the `HasDimension` typeclass.- dynamicDimension :: a -> DynamicDimension- default dynamicDimension :: (HasDimension a) => a -> DynamicDimension- dynamicDimension = SomeDimension . dimension---- | Dimensional values inhabit this class, which allows access to a term-level representation of their dimension.-class HasDynamicDimension a => HasDimension a where- -- | Obtains a term-level representation of a value's dimension.- dimension :: a -> Dimension'--instance HasDynamicDimension DynamicDimension where- dynamicDimension = id--instance HasDynamicDimension Dimension' where--instance HasDimension Dimension' where- dimension = id---- | Combines two 'DynamicDimension's, determining the 'DynamicDimension' of a quantity that must--- match both inputs.------ This is the lattice meet operation for 'DynamicDimension'.-matchDimensions :: DynamicDimension -> DynamicDimension -> DynamicDimension-matchDimensions AnyDimension AnyDimension = AnyDimension-matchDimensions d@(SomeDimension _) AnyDimension = d-matchDimensions AnyDimension d@(SomeDimension _) = d-matchDimensions (SomeDimension d1) (SomeDimension d2) | d1 == d2 = SomeDimension d1-matchDimensions _ _ = NoDimension---- | Determines if a value that has a 'DynamicDimension' is compatible with a specified 'Dimension''.-isCompatibleWith :: (HasDynamicDimension a) => a -> Dimension' -> Bool-isCompatibleWith = f . dynamicDimension- where- f AnyDimension _ = True- f (SomeDimension d1) d2 | d1 == d2 = True- f _ _ = False---- | Determines if a value that has a 'DynamicDimension' in fact has any valid dimension at all.-hasSomeDimension :: (HasDynamicDimension a) => a -> Bool-hasSomeDimension = (/= NoDimension) . dynamicDimension---- | The dimension of dimensionless values.-dOne :: Dimension'-dOne = Dim' 0 0 0 0 0 0 0--dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity :: Dimension'-dLength = Dim' 1 0 0 0 0 0 0-dMass = Dim' 0 1 0 0 0 0 0-dTime = Dim' 0 0 1 0 0 0 0-dElectricCurrent = Dim' 0 0 0 1 0 0 0-dThermodynamicTemperature = Dim' 0 0 0 0 1 0 0-dAmountOfSubstance = Dim' 0 0 0 0 0 1 0-dLuminousIntensity = Dim' 0 0 0 0 0 0 1--{--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.--}--infixr 8 ^-infixl 7 *, /---- | Forms the product of two dimensions.-(*) :: Dimension' -> Dimension' -> Dimension'-(Dim' l m t i th n j) * (Dim' l' m' t' i' th' n' j') = Dim' (l + l') (m + m') (t + t') (i + i') (th + th') (n + n') (j + j')---- | Forms the quotient of two dimensions.-(/) :: Dimension' -> Dimension' -> Dimension'-(Dim' l m t i th n j) / (Dim' l' m' t' i' th' n' j') = Dim' (l - l') (m - m') (t - t') (i - i') (th - th') (n - n') (j - j')---- | Raises a dimension to an integer power.-(^) :: Dimension' -> Int -> Dimension'-(Dim' l m t i th n j) ^ x = Dim' (x P.* l) (x P.* m) (x P.* t) (x P.* i) (x P.* th) (x P.* n) (x P.* j)---- | Forms the reciprocal of a dimension.-recip :: Dimension' -> Dimension'-recip = (dOne /)---- | Takes the nth root of a dimension, if it exists.------ n must not be zero.------ prop> nroot (negate n) d == nroot n (recip d)-nroot :: Int -> Dimension' -> Maybe Dimension'-nroot n d | n /= 0 && all ((== 0) . snd) ds = fromList . fmap fst $ ds- | otherwise = Nothing- where- ds = fmap (`divMod` n) . asList $ d---- | Takes the square root of a dimension, if it exists.------ prop> sqrt d == nroot 2 d-sqrt :: Dimension' -> Maybe Dimension'-sqrt = nroot 2---- | Takes the cube root of a dimension, if it exists.------ prop> cbrt d == nroot 3 d-cbrt :: Dimension' -> Maybe Dimension'-cbrt = nroot 3---- | Converts a dimension to a list of 7 integers, representing the exponent associated with each--- of the 7 SI base dimensions in the standard order.-asList :: Dimension' -> [Int]-asList (Dim' l m t i th n j) = [l, m, t, i, th, n, j]--fromList :: [Int] -> Maybe Dimension'-fromList [l, m, t, i, th, n, j] = Just $ Dim' l m t i th n j-fromList _ = Nothing+{-# OPTIONS_HADDOCK not-home, show-extensions #-} + +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DefaultSignatures #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +This module defines physical dimensions expressed in terms of +the SI base dimensions, including arithmetic. + +-} +module Numeric.Units.Dimensional.Dimensions.TermLevel +( + -- * Type + Dimension'(..), + -- * Access to Dimension of Dimensional Values + HasDimension(..), HasDynamicDimension(..), DynamicDimension(..), + -- * Dimension Arithmetic + (*), (/), (^), recip, nroot, sqrt, cbrt, + -- * Synonyms for Base Dimensions + dOne, + dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity, + -- * Deconstruction + asList, + -- * Examining Dynamic Dimensions + matchDimensions, isCompatibleWith, hasSomeDimension +) +where + +import Control.DeepSeq +import Data.Data +import Data.Semigroup (Semigroup(..)) +import Data.Monoid (Monoid(..)) +import GHC.Generics +import Prelude (id, all, fst, snd, fmap, otherwise, divMod, ($), (+), (-), (.), (&&), Int, Show, Eq(..), Ord(..), Maybe(..), Bool(..)) +import qualified Prelude as P + +-- $setup +-- >>> import Prelude (negate) +-- >>> import Control.Applicative +-- >>> import Test.QuickCheck.Arbitrary +-- >>> instance Arbitrary Dimension' where arbitrary = Dim' <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary + +-- | A physical dimension, encoded as 7 integers, representing a factorization of the dimension into the +-- 7 SI base dimensions. By convention they are stored in the same order as +-- in the 'Numeric.Units.Dimensional.Dimensions.TypeLevel.Dimension' data kind. +data Dimension' = Dim' !Int !Int !Int !Int !Int !Int !Int + deriving (Show, Eq, Ord, Data, Generic, Typeable) + +instance NFData Dimension' where + rnf !_ = () -- The Dimension' constructor is already fully strict. + +instance Semigroup Dimension' where + (<>) = (*) + +-- | The monoid of dimensions under multiplication. +instance Monoid Dimension' where + mempty = dOne + mappend = (Data.Semigroup.<>) + +-- | The dimension of a dynamic value, which may not have any dimension at all. +data DynamicDimension = NoDimension -- ^ The value has no valid dimension. + | SomeDimension Dimension' -- ^ The value has the given dimension. + | AnyDimension -- ^ The value may be interpreted as having any dimension. + deriving (Eq, Ord, Show, Data, Generic, Typeable) + +instance NFData DynamicDimension where + +-- | Dimensional values, or those that are only possibly dimensional, inhabit this class, +-- which allows access to a term-level representation of their dimension. +class HasDynamicDimension a where + -- | Gets the 'DynamicDimension of a dynamic dimensional value, which may be 'NoDimension' if it does not represent + -- a dimensional value of any 'Dimension'. + -- + -- A default implementation is available for types that are also in the `HasDimension` typeclass. + dynamicDimension :: a -> DynamicDimension + default dynamicDimension :: (HasDimension a) => a -> DynamicDimension + dynamicDimension = SomeDimension . dimension + +-- | Dimensional values inhabit this class, which allows access to a term-level representation of their dimension. +class HasDynamicDimension a => HasDimension a where + -- | Obtains a term-level representation of a value's dimension. + dimension :: a -> Dimension' + +instance HasDynamicDimension DynamicDimension where + dynamicDimension = id + +instance HasDynamicDimension Dimension' where + +instance HasDimension Dimension' where + dimension = id + +-- | Combines two 'DynamicDimension's, determining the 'DynamicDimension' of a quantity that must +-- match both inputs. +-- +-- This is the lattice meet operation for 'DynamicDimension'. +matchDimensions :: DynamicDimension -> DynamicDimension -> DynamicDimension +matchDimensions AnyDimension AnyDimension = AnyDimension +matchDimensions d@(SomeDimension _) AnyDimension = d +matchDimensions AnyDimension d@(SomeDimension _) = d +matchDimensions (SomeDimension d1) (SomeDimension d2) | d1 == d2 = SomeDimension d1 +matchDimensions _ _ = NoDimension + +-- | Determines if a value that has a 'DynamicDimension' is compatible with a specified 'Dimension''. +isCompatibleWith :: (HasDynamicDimension a) => a -> Dimension' -> Bool +isCompatibleWith = f . dynamicDimension + where + f AnyDimension _ = True + f (SomeDimension d1) d2 | d1 == d2 = True + f _ _ = False + +-- | Determines if a value that has a 'DynamicDimension' in fact has any valid dimension at all. +hasSomeDimension :: (HasDynamicDimension a) => a -> Bool +hasSomeDimension = (/= NoDimension) . dynamicDimension + +-- | The dimension of dimensionless values. +dOne :: Dimension' +dOne = Dim' 0 0 0 0 0 0 0 + +dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity :: Dimension' +dLength = Dim' 1 0 0 0 0 0 0 +dMass = Dim' 0 1 0 0 0 0 0 +dTime = Dim' 0 0 1 0 0 0 0 +dElectricCurrent = Dim' 0 0 0 1 0 0 0 +dThermodynamicTemperature = Dim' 0 0 0 0 1 0 0 +dAmountOfSubstance = Dim' 0 0 0 0 0 1 0 +dLuminousIntensity = Dim' 0 0 0 0 0 0 1 + +{- +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. +-} + +infixr 8 ^ +infixl 7 *, / + +-- | Forms the product of two dimensions. +(*) :: Dimension' -> Dimension' -> Dimension' +(Dim' l m t i th n j) * (Dim' l' m' t' i' th' n' j') = Dim' (l + l') (m + m') (t + t') (i + i') (th + th') (n + n') (j + j') + +-- | Forms the quotient of two dimensions. +(/) :: Dimension' -> Dimension' -> Dimension' +(Dim' l m t i th n j) / (Dim' l' m' t' i' th' n' j') = Dim' (l - l') (m - m') (t - t') (i - i') (th - th') (n - n') (j - j') + +-- | Raises a dimension to an integer power. +(^) :: Dimension' -> Int -> Dimension' +(Dim' l m t i th n j) ^ x = Dim' (x P.* l) (x P.* m) (x P.* t) (x P.* i) (x P.* th) (x P.* n) (x P.* j) + +-- | Forms the reciprocal of a dimension. +recip :: Dimension' -> Dimension' +recip = (dOne /) + +-- | Takes the nth root of a dimension, if it exists. +-- +-- n must not be zero. +-- +-- prop> nroot (negate n) d == nroot n (recip d) +nroot :: Int -> Dimension' -> Maybe Dimension' +nroot n d | n /= 0 && all ((== 0) . snd) ds = fromList . fmap fst $ ds + | otherwise = Nothing + where + ds = fmap (`divMod` n) . asList $ d + +-- | Takes the square root of a dimension, if it exists. +-- +-- prop> sqrt d == nroot 2 d +sqrt :: Dimension' -> Maybe Dimension' +sqrt = nroot 2 + +-- | Takes the cube root of a dimension, if it exists. +-- +-- prop> cbrt d == nroot 3 d +cbrt :: Dimension' -> Maybe Dimension' +cbrt = nroot 3 + +-- | Converts a dimension to a list of 7 integers, representing the exponent associated with each +-- of the 7 SI base dimensions in the standard order. +asList :: Dimension' -> [Int] +asList (Dim' l m t i th n j) = [l, m, t, i, th, n, j] + +fromList :: [Int] -> Maybe Dimension' +fromList [l, m, t, i, th, n, j] = Just $ Dim' l m t i th n j +fromList _ = Nothing
src/Numeric/Units/Dimensional/Dimensions/TypeLevel.hs view
@@ -1,160 +1,160 @@-{-# OPTIONS_HADDOCK not-home, show-extensions #-}--{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--This module defines type-level physical dimensions expressed in terms of-the SI base dimensions using 'Numeric.NumType.DK.NumType' for type-level integers.--Type-level arithmetic, synonyms for the base dimensions, and conversion to the term-level are included.--}-module Numeric.Units.Dimensional.Dimensions.TypeLevel-(- -- * Kind of Type-Level Dimensions- type Dimension(..),- -- * Dimension Arithmetic- type (*), type (/), type (^), type Recip, type NRoot, type Sqrt, type Cbrt,- -- * Synonyms for Base Dimensions- DOne,- DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity,- -- * Conversion to Term Level- type KnownDimension-)-where--import Data.Proxy-import Numeric.NumType.DK.Integers- ( TypeInt (Zero, Pos1, Pos2, Pos3), type (+), type (-)- , KnownTypeInt, toNum- )-import qualified Numeric.NumType.DK.Integers as N-import Numeric.Units.Dimensional.Dimensions.TermLevel---- | Represents a physical dimension in the basis of the 7 SI base dimensions,--- where the respective dimensions are represented by type variables--- using the following convention:------ * l: Length--- * m: Mass--- * t: Time--- * i: Electric current--- * th: Thermodynamic temperature--- * n: Amount of substance--- * j: Luminous intensity------ For the equivalent term-level representation, see 'Dimension''-data Dimension = Dim TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt---- | The type-level dimension of dimensionless values.-type DOne = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero-type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero-type DMass = 'Dim 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero-type DTime = 'Dim 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero-type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero-type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero-type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero-type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1--{--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.--}--infixr 8 ^-infixl 7 *, /---- | Multiplication of dimensions corresponds to adding of the base--- dimensions' exponents.-type family (a::Dimension) * (b::Dimension) where- DOne * d = d- d * DOne = d- ('Dim l m t i th n j) * ('Dim l' m' t' i' th' n' j')- = 'Dim (l + l') (m + m') (t + t') (i + i') (th + th') (n + n') (j + j')---- | Division of dimensions corresponds to subtraction of the base--- dimensions' exponents.-type family (a::Dimension) / (d::Dimension) where- d / DOne = d- d / d = DOne- ('Dim l m t i th n j) / ('Dim l' m' t' i' th' n' j')- = 'Dim (l - l') (m - m') (t - t') (i - i') (th - th') (n - n') (j - j')---- | The reciprocal of a dimension is defined as the result of dividing 'DOne' by it,--- or of negating each of the base dimensions' exponents.-type Recip (d :: Dimension) = DOne / d---- | Powers of dimensions corresponds to multiplication of the base--- dimensions' exponents by the exponent.------ We limit ourselves to integer powers of Dimensionals as fractional--- powers make little physical sense.-type family (d::Dimension) ^ (x::TypeInt) where- DOne ^ x = DOne- d ^ 'Zero = DOne- d ^ 'Pos1 = d- ('Dim l m t i th n j) ^ x- = 'Dim (l N.* x) (m N.* x) (t N.* x) (i N.* x) (th N.* x) (n N.* x) (j N.* x)---- | Roots of dimensions corresponds to division of the base dimensions'--- exponents by the order of the root.-type family NRoot (d::Dimension) (x::TypeInt) where- NRoot DOne x = DOne- NRoot d 'Pos1 = d- NRoot ('Dim l m t i th n j) x- = 'Dim (l N./ x) (m N./ x) (t N./ x) (i N./ x) (th N./ x) (n N./ x) (j N./ x)---- | Square root is a special case of 'NRoot' with order 2.-type Sqrt d = NRoot d 'Pos2---- | Cube root is a special case of 'NRoot' with order 3.-type Cbrt d = NRoot d 'Pos3---- | A KnownDimension is one for which we can construct a term-level representation.--- Each validly constructed type of kind 'Dimension' has a 'KnownDimension' instance.------ While 'KnownDimension' is a constraint synonym, the presence of @'KnownDimension' d@ in--- a context allows use of @'dimension' :: 'Proxy' d -> 'Dimension''@.-type KnownDimension (d :: Dimension) = HasDimension (Proxy d)--instance ( KnownTypeInt l- , KnownTypeInt m- , KnownTypeInt t- , KnownTypeInt i- , KnownTypeInt th- , KnownTypeInt n- , KnownTypeInt j- ) => HasDynamicDimension (Proxy ('Dim l m t i th n j))- where--instance ( KnownTypeInt l- , KnownTypeInt m- , KnownTypeInt t- , KnownTypeInt i- , KnownTypeInt th- , KnownTypeInt n- , KnownTypeInt j- ) => HasDimension (Proxy ('Dim l m t i th n j))- where- dimension _ = Dim'- (toNum (Proxy :: Proxy l))- (toNum (Proxy :: Proxy m))- (toNum (Proxy :: Proxy t))- (toNum (Proxy :: Proxy i))- (toNum (Proxy :: Proxy th))- (toNum (Proxy :: Proxy n))- (toNum (Proxy :: Proxy j))+{-# OPTIONS_HADDOCK not-home, show-extensions #-} + +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +This module defines type-level physical dimensions expressed in terms of +the SI base dimensions using 'Numeric.NumType.DK.NumType' for type-level integers. + +Type-level arithmetic, synonyms for the base dimensions, and conversion to the term-level are included. +-} +module Numeric.Units.Dimensional.Dimensions.TypeLevel +( + -- * Kind of Type-Level Dimensions + type Dimension(..), + -- * Dimension Arithmetic + type (*), type (/), type (^), type Recip, type NRoot, type Sqrt, type Cbrt, + -- * Synonyms for Base Dimensions + DOne, + DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity, + -- * Conversion to Term Level + type KnownDimension +) +where + +import Data.Proxy +import Numeric.NumType.DK.Integers + ( TypeInt (Zero, Pos1, Pos2, Pos3), type (+), type (-) + , KnownTypeInt, toNum + ) +import qualified Numeric.NumType.DK.Integers as N +import Numeric.Units.Dimensional.Dimensions.TermLevel + +-- | Represents a physical dimension in the basis of the 7 SI base dimensions, +-- where the respective dimensions are represented by type variables +-- using the following convention: +-- +-- * l: Length +-- * m: Mass +-- * t: Time +-- * i: Electric current +-- * th: Thermodynamic temperature +-- * n: Amount of substance +-- * j: Luminous intensity +-- +-- For the equivalent term-level representation, see 'Dimension'' +data Dimension = Dim TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt + +-- | The type-level dimension of dimensionless values. +type DOne = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero +type DLength = 'Dim 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero +type DMass = 'Dim 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero +type DTime = 'Dim 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero 'Zero +type DElectricCurrent = 'Dim 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero +type DThermodynamicTemperature = 'Dim 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero 'Zero +type DAmountOfSubstance = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero +type DLuminousIntensity = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 + +{- +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. +-} + +infixr 8 ^ +infixl 7 *, / + +-- | Multiplication of dimensions corresponds to adding of the base +-- dimensions' exponents. +type family (a::Dimension) * (b::Dimension) where + DOne * d = d + d * DOne = d + ('Dim l m t i th n j) * ('Dim l' m' t' i' th' n' j') + = 'Dim (l + l') (m + m') (t + t') (i + i') (th + th') (n + n') (j + j') + +-- | Division of dimensions corresponds to subtraction of the base +-- dimensions' exponents. +type family (a::Dimension) / (d::Dimension) where + d / DOne = d + d / d = DOne + ('Dim l m t i th n j) / ('Dim l' m' t' i' th' n' j') + = 'Dim (l - l') (m - m') (t - t') (i - i') (th - th') (n - n') (j - j') + +-- | The reciprocal of a dimension is defined as the result of dividing 'DOne' by it, +-- or of negating each of the base dimensions' exponents. +type Recip (d :: Dimension) = DOne / d + +-- | Powers of dimensions corresponds to multiplication of the base +-- dimensions' exponents by the exponent. +-- +-- We limit ourselves to integer powers of Dimensionals as fractional +-- powers make little physical sense. +type family (d::Dimension) ^ (x::TypeInt) where + DOne ^ x = DOne + d ^ 'Zero = DOne + d ^ 'Pos1 = d + ('Dim l m t i th n j) ^ x + = 'Dim (l N.* x) (m N.* x) (t N.* x) (i N.* x) (th N.* x) (n N.* x) (j N.* x) + +-- | Roots of dimensions corresponds to division of the base dimensions' +-- exponents by the order of the root. +type family NRoot (d::Dimension) (x::TypeInt) where + NRoot DOne x = DOne + NRoot d 'Pos1 = d + NRoot ('Dim l m t i th n j) x + = 'Dim (l N./ x) (m N./ x) (t N./ x) (i N./ x) (th N./ x) (n N./ x) (j N./ x) + +-- | Square root is a special case of 'NRoot' with order 2. +type Sqrt d = NRoot d 'Pos2 + +-- | Cube root is a special case of 'NRoot' with order 3. +type Cbrt d = NRoot d 'Pos3 + +-- | A KnownDimension is one for which we can construct a term-level representation. +-- Each validly constructed type of kind 'Dimension' has a 'KnownDimension' instance. +-- +-- While 'KnownDimension' is a constraint synonym, the presence of @'KnownDimension' d@ in +-- a context allows use of @'dimension' :: 'Proxy' d -> 'Dimension''@. +type KnownDimension (d :: Dimension) = HasDimension (Proxy d) + +instance ( KnownTypeInt l + , KnownTypeInt m + , KnownTypeInt t + , KnownTypeInt i + , KnownTypeInt th + , KnownTypeInt n + , KnownTypeInt j + ) => HasDynamicDimension (Proxy ('Dim l m t i th n j)) + where + +instance ( KnownTypeInt l + , KnownTypeInt m + , KnownTypeInt t + , KnownTypeInt i + , KnownTypeInt th + , KnownTypeInt n + , KnownTypeInt j + ) => HasDimension (Proxy ('Dim l m t i th n j)) + where + dimension _ = Dim' + (toNum (Proxy :: Proxy l)) + (toNum (Proxy :: Proxy m)) + (toNum (Proxy :: Proxy t)) + (toNum (Proxy :: Proxy i)) + (toNum (Proxy :: Proxy th)) + (toNum (Proxy :: Proxy n)) + (toNum (Proxy :: Proxy j))
src/Numeric/Units/Dimensional/Dynamic.hs view
@@ -1,351 +1,352 @@-{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only?--Defines types for manipulation of units and quantities without phantom types for their dimensions.--}--{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Numeric.Units.Dimensional.Dynamic-(- -- * Dynamic Quantities- AnyQuantity-, DynQuantity-, Demotable-, Promotable-, HasDynamicDimension(..), DynamicDimension(..)-, promoteQuantity, demoteQuantity-, (*~), (/~), invalidQuantity, polydimensionalZero- -- * Dynamic Units-, AnyUnit-, demoteUnit, promoteUnit, demoteUnit'-, siUnit, anyUnitName- -- ** Arithmetic on Dynamic Units-, (*), (/), (^), recip, applyPrefix-) where--import Control.DeepSeq-import Control.Monad-import Data.Data-import Data.ExactPi-import Data.Semigroup (Semigroup(..))-import Data.Monoid (Monoid(..))-import GHC.Generics-import Prelude (Eq(..), Num, Fractional, Floating, Show(..), Bool(..), Maybe(..), (.), ($), (++), (&&), id, otherwise, error)-import qualified Prelude as P-import Numeric.Units.Dimensional hiding ((*~), (/~), (*), (/), (^), recip, nroot, siUnit)-import qualified Numeric.Units.Dimensional as Dim-import Numeric.Units.Dimensional.Coercion-import Numeric.Units.Dimensional.UnitNames (UnitName, baseUnitName)-import qualified Numeric.Units.Dimensional.UnitNames.InterchangeNames as I-import qualified Numeric.Units.Dimensional.UnitNames as N-import Numeric.Units.Dimensional.Dimensions.TermLevel (HasDynamicDimension(..), DynamicDimension(..), matchDimensions, isCompatibleWith)-import qualified Numeric.Units.Dimensional.Dimensions.TermLevel as D---- | The class of types that can be used to model 'Quantity's that are certain to have a value with--- some dimension.-class Demotable (q :: * -> *) where- demotableOut :: q a -> AnyQuantity a---- | The class of types that can be used to model 'Quantity's whose 'Dimension's are--- only known dynamically.-class Promotable (q :: * -> *) where- promotableIn :: AnyQuantity a -> q a- promotableOut :: q a -> DynQuantity a---- | Forgets information about a 'Quantity' or 'AnyQuantity', yielding an 'AnyQuantity' or a 'DynQuantity'.-demoteQuantity :: (Demotable q, Promotable d) => q a -> d a-demoteQuantity = promotableIn . demotableOut---- | Converts a dynamic quantity such as an 'AnyQuantity' or a 'DynQuantity' into a--- 'Quantity', or to 'Nothing' if the dynamic quantity cannot be represented in the--- narrower result type.-promoteQuantity :: forall a d q.(Promotable q, KnownDimension d) => q a -> Maybe (Quantity d a)-promoteQuantity = promoteQ . promotableOut- where- dim' = dimension (Proxy :: Proxy d)- promoteQ (DynQuantity d v) | d `isCompatibleWith` dim' = Just . Quantity $ v- | otherwise = Nothing--instance (KnownDimension d) => Demotable (Quantity d) where- demotableOut q@(Quantity x) = AnyQuantity (dimension q) x---- | A 'Quantity' whose 'Dimension' is only known dynamically.-data AnyQuantity a = AnyQuantity !Dimension' !a- deriving (Eq, Data, Generic, Generic1, Typeable)--instance (Show a) => Show (AnyQuantity a) where- show (AnyQuantity d a) | d == D.dOne = show a- | otherwise = (show a) ++ " " ++ (show . baseUnitName $ d)--instance HasDynamicDimension (AnyQuantity a) where--instance HasDimension (AnyQuantity a) where- dimension (AnyQuantity d _) = d--instance NFData a => NFData (AnyQuantity a) -- instance is derived from Generic instance--instance Promotable AnyQuantity where- promotableIn = id- promotableOut (AnyQuantity d a) = DynQuantity (SomeDimension d) a--instance Demotable AnyQuantity where- demotableOut = id---- | 'AnyQuantity's form a 'Semigroup' under multiplication, but not under addition because--- they may not be added together if their dimensions do not match.-instance Num a => Semigroup (AnyQuantity a) where- (AnyQuantity d1 a1) <> (AnyQuantity d2 a2) = AnyQuantity (d1 D.* d2) (a1 P.* a2)---- | 'AnyQuantity's form a 'Monoid' under multiplication, but not under addition because--- they may not be added together if their dimensions do not match.-instance Num a => Monoid (AnyQuantity a) where- mempty = demoteQuantity (1 Dim.*~ one)- mappend = (Data.Semigroup.<>)---- | Possibly a 'Quantity' whose 'Dimension' is only known dynamically.------ By modeling the absence of a value, this type differs from 'AnyQuantity' in that it may--- not be a 'Quantity' of any 'Dimension' whatsoever, but in exchange it gains instances--- for the common numeric classes. It's therefore useful for manipulating, and not merely storing,--- quantities of unknown dimension.------ This type also contains a 'polydimensionalZero', representing zero value of any dimension.------ Note that the 'Eq' instance for 'DynQuantity' equates all representations of an invalid value,--- and also does not equate polydimensional zero with zero of any specific dimension.-data DynQuantity a = DynQuantity !DynamicDimension a -- we can't have strictness annotation on a as it is sometimes undefined- deriving (Data, Generic, Generic1, Typeable)--instance Eq a => Eq (DynQuantity a) where- (DynQuantity NoDimension _) == (DynQuantity NoDimension _) = True -- all invalid quantities are equal- (DynQuantity NoDimension _) == _ = False -- invalid quanties are not equal to any other quantity- _ == (DynQuantity NoDimension _) = False- (DynQuantity d1 v1) == (DynQuantity d2 v2) = d1 == d2 && v1 == v2--instance NFData a => NFData (DynQuantity a) -- instance is derived from Generic instance--instance Show a => Show (DynQuantity a) where- show (DynQuantity NoDimension _) = "invalidQuantity"- show (DynQuantity AnyDimension v) = show v- show (DynQuantity (SomeDimension d) v) = show $ AnyQuantity d v--instance Promotable DynQuantity where- promotableIn (AnyQuantity d a) = DynQuantity (SomeDimension d) a- promotableOut = id--instance HasDynamicDimension (DynQuantity a) where- dynamicDimension (DynQuantity d _) = d--instance Num a => Num (DynQuantity a) where- x + y = liftDQ2 matchDimensions (P.+) x y- x - y = liftDQ2 matchDimensions (P.-) x y- x * y = liftDQ2 (valid2 (D.*)) (P.*) x y- negate = liftDQ id P.negate- abs = liftDQ id P.abs- signum = liftDQ (constant D.dOne) P.signum- fromInteger = demoteQuantity . (Dim.*~ one) . P.fromInteger--instance Fractional a => Fractional (DynQuantity a) where- x / y = liftDQ2 (valid2 (D./)) (P./) x y- recip = liftDQ (valid D.recip) P.recip- fromRational = demoteQuantity . (Dim.*~ one) . P.fromRational--instance Floating a => Floating (DynQuantity a) where- pi = demoteQuantity pi- exp = liftDimensionless P.exp- log = liftDimensionless P.log- sqrt = liftDQ (whenValid $ D.nroot 2) P.sqrt- (**) = liftDQ2 (matchDimensions3 $ SomeDimension D.dOne) (P.**)- logBase = liftDQ2 (matchDimensions3 $ SomeDimension D.dOne) P.logBase- sin = liftDimensionless P.sin- cos = liftDimensionless P.cos- tan = liftDimensionless P.tan- asin = liftDimensionless P.asin- acos = liftDimensionless P.acos- atan = liftDimensionless P.atan- sinh = liftDimensionless P.sinh- cosh = liftDimensionless P.cosh- tanh = liftDimensionless P.tanh- asinh = liftDimensionless P.asinh- acosh = liftDimensionless P.acosh- atanh = liftDimensionless P.atanh---- | 'DynQuantity's form a 'Semigroup' under multiplication, but not under addition because--- they may not be added together if their dimensions do not match.-instance Num a => Semigroup (DynQuantity a) where- (<>) = (P.*)---- | 'DynQuantity's form a 'Monoid' under multiplication, but not under addition because--- they may not be added together if their dimensions do not match.-instance Num a => Monoid (DynQuantity a) where- mempty = demoteQuantity (1 Dim.*~ one)- mappend = (Data.Semigroup.<>)---- | A 'DynQuantity' which does not correspond to a value of any dimension.-invalidQuantity :: DynQuantity a-invalidQuantity = DynQuantity NoDimension $ error "Attempt to evaluate the value of an invalid quantity."---- | A 'DynQuantity' which corresponds to zero value of any dimension.------ When combined through arithmetic with other 'DynQuantity's, inference is performed. For example,--- adding a length to polydimensional zero produces that length. Adding two polydimensional zeros produces another.--- Taking the sine of a polydimensional zero interprets it as a dimensionless zero and produces a dimensionless result.------ Note that division by 'polydimensionalZero' produces a polydimensional result, which may be an error or some representation--- of infinity, as determined by the underlying arithmetic type. This behavior was chosen for consistency with the behavior of division--- by zero 'DynQuantity's of a specific dimension.-polydimensionalZero :: (Num a) => DynQuantity a-polydimensionalZero = DynQuantity AnyDimension 0---- Lifts a function which is only valid on dimensionless quantities into a function on DynQuantitys.-liftDimensionless :: (a -> a) -> DynQuantity a -> DynQuantity a-liftDimensionless f = liftDQ (matchDimensions $ SomeDimension D.dOne) f---- Lifts a function on values into a function on DynQuantitys.-liftDQ :: (DynamicDimension -> DynamicDimension) -- ^ How the function operates on dimensions.- -> (a -> a) -- ^ How the function operates on values.- -> DynQuantity a -> DynQuantity a-liftDQ fd fv (DynQuantity d v) = case fd d of- NoDimension -> invalidQuantity- d' -> DynQuantity d' $ fv v---- Lifts a function on values into a function on DynQuantitys.------ This works by treating polydimensional zeros as dimensionless zeros. If that is not the desired behavior,--- handle polydimensional zeros first and then call this function.-liftDQ2 :: (DynamicDimension -> DynamicDimension -> DynamicDimension)- -> (a -> a -> a)- -> DynQuantity a -> DynQuantity a -> DynQuantity a-liftDQ2 fd fv (DynQuantity d1 v1) (DynQuantity d2 v2) = case fd d1 d2 of- NoDimension -> invalidQuantity- d' -> DynQuantity d' $ fv v1 v2---- Transforms a dynamic dimension in a way which is always valid-valid :: (Dimension' -> Dimension') -> DynamicDimension -> DynamicDimension-valid _ AnyDimension = AnyDimension-valid f (SomeDimension d) = SomeDimension (f d)-valid _ NoDimension = NoDimension--whenValid :: (Dimension' -> Maybe Dimension') -> DynamicDimension -> DynamicDimension-whenValid _ AnyDimension = AnyDimension-whenValid f (SomeDimension d) | Just d' <- f d = SomeDimension d'-whenValid _ _ = NoDimension--constant :: Dimension' -> DynamicDimension -> DynamicDimension-constant d AnyDimension = SomeDimension d-constant d (SomeDimension _) = SomeDimension d-constant _ _ = NoDimension---- Transforms two dynamic dimensions in a way which is always valid-valid2 :: (Dimension' -> Dimension' -> Dimension') -> DynamicDimension -> DynamicDimension -> DynamicDimension-valid2 _ AnyDimension (SomeDimension _) = AnyDimension-valid2 _ (SomeDimension _) AnyDimension = AnyDimension-valid2 _ AnyDimension AnyDimension = AnyDimension-valid2 f (SomeDimension d1) (SomeDimension d2) = SomeDimension (f d1 d2)-valid2 _ _ _ = NoDimension--matchDimensions3 :: DynamicDimension -> DynamicDimension -> DynamicDimension -> DynamicDimension-matchDimensions3 x y z = matchDimensions x (matchDimensions y z)---- | A 'Unit' whose 'Dimension' is only known dynamically.-data AnyUnit = AnyUnit Dimension' (UnitName 'NonMetric) ExactPi- deriving (Generic, Typeable)--instance Show AnyUnit where- show (AnyUnit _ n e) = (show n) ++ " =def= " ++ (show e) ++ " of the SI base unit"--instance HasDynamicDimension AnyUnit where--instance HasDimension AnyUnit where- dimension (AnyUnit d _ _) = d--instance I.HasInterchangeName AnyUnit where- interchangeName (AnyUnit _ n _) = I.interchangeName n---- | 'AnyUnit's form a 'Semigroup' under multiplication.-instance Semigroup AnyUnit where- (<>) = (Numeric.Units.Dimensional.Dynamic.*)---- | 'AnyUnit's form a 'Monoid' under multiplication.-instance Monoid AnyUnit where- mempty = demoteUnit' one- mappend = (Data.Semigroup.<>)--anyUnitName :: AnyUnit -> UnitName 'NonMetric-anyUnitName (AnyUnit _ n _) = n---- | The dynamic SI coherent unit of a given dimension.-siUnit :: Dimension' -> AnyUnit-siUnit d = AnyUnit d (baseUnitName d) 1---- | Converts a 'Unit' of statically known 'Dimension' into an 'AnyUnit'.-demoteUnit :: forall m d a.(KnownDimension d) => Unit m d a -> AnyUnit-demoteUnit u = AnyUnit dim (name $ weaken u) (exactValue u)- where- dim = dimension (Proxy :: Proxy d)---- | Converts a 'Unit' of statically known 'Dimension' into an 'AnyUnit'.------ This is the same as the more general 'demoteUnit' but is useful in certain circumstances to avoid--- needlessly introducing an ambiguous type variable.-demoteUnit' :: (KnownDimension d) => Unit m d ExactPi -> AnyUnit-demoteUnit' = demoteUnit---- | Converts an 'AnyUnit' into a 'Unit' of statically known 'Dimension', or 'Nothing' if the dimension does not match.------ The result is represented in 'ExactPi', conversion to other representations is possible using 'changeRepApproximate'.------ The result is always tagged as 'NonMetric', conversion to a 'Metric' unit can be attempted using 'strengthen'.-promoteUnit :: forall d.(KnownDimension d) => AnyUnit -> Maybe (Unit 'NonMetric d ExactPi)-promoteUnit (AnyUnit dim n e) | dim == dim' = Just $ mkUnitR n e Dim.siUnit- | otherwise = Nothing- where- dim' = dimension (Proxy :: Proxy d)---- | Forms the reciprocal of a dynamic unit.-recip :: AnyUnit -> AnyUnit-recip (AnyUnit d n e) = AnyUnit (D.recip d) (N.nOne N./ n) (P.recip e)---- | Forms the product of two dynamic units.-(*) :: AnyUnit -> AnyUnit -> AnyUnit-(AnyUnit d1 n1 e1) * (AnyUnit d2 n2 e2) = AnyUnit (d1 D.* d2) (n1 N.* n2) (e1 P.* e2)---- | Forms the quotient of two dynamic units.-(/) :: AnyUnit -> AnyUnit -> AnyUnit-(AnyUnit d1 n1 e1) / (AnyUnit d2 n2 e2) = AnyUnit (d1 D./ d2) (n1 N./ n2) (e1 P./ e2)---- | Raises a dynamic unit to an integer power.-(^) :: (P.Integral a) => AnyUnit -> a -> AnyUnit-(AnyUnit d n e) ^ x = AnyUnit (d D.^ P.fromIntegral x) (n N.^ P.fromIntegral x) (e P.^^ x)---- | Applies a prefix to a dynamic unit.--- Returns 'Nothing' if the 'Unit' was 'NonMetric' and thus could not accept a prefix.-applyPrefix :: N.Prefix -> AnyUnit -> Maybe AnyUnit-applyPrefix p (AnyUnit d n e) = do- n' <- N.strengthen n- let n'' = N.applyPrefix p n'- let e' = (P.fromRational $ N.scaleFactor p) P.* e- return $ AnyUnit d n'' e'---- | Forms a dynamic quantity by multipliying a number and a dynamic unit.-(*~) :: (Floating a, Promotable q) => a -> AnyUnit -> q a-x *~ (AnyUnit d _ e) = promotableIn $ AnyQuantity d (x P.* approximateValue e)---- | Divides a dynamic quantity by a dynamic unit, obtaining the numerical value of the quantity--- expressed in that unit if they are of the same physical dimension, or 'Nothing' otherwise.-(/~) :: (Floating a, Promotable q) => q a -> AnyUnit -> Maybe a-x /~ (AnyUnit d _ e) = case promotableOut x of- DynQuantity d' x' | d' `isCompatibleWith` d -> Just $ x' P./ approximateValue e- | otherwise -> Nothing+{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only? + +Defines types for manipulation of units and quantities without phantom types for their dimensions. +-} + +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Numeric.Units.Dimensional.Dynamic +( + -- * Dynamic Quantities + AnyQuantity +, DynQuantity +, Demotable +, Promotable +, HasDynamicDimension(..), DynamicDimension(..) +, promoteQuantity, demoteQuantity +, (*~), (/~), invalidQuantity, polydimensionalZero + -- * Dynamic Units +, AnyUnit +, demoteUnit, promoteUnit, demoteUnit' +, siUnit, anyUnitName + -- ** Arithmetic on Dynamic Units +, (*), (/), (^), recip, applyPrefix +) where + +import Control.DeepSeq +import Control.Monad +import Data.Data +import Data.ExactPi +import Data.Kind +import Data.Semigroup (Semigroup(..)) +import Data.Monoid (Monoid(..)) +import GHC.Generics +import Prelude (Eq(..), Num, Fractional, Floating, Show(..), Bool(..), Maybe(..), (.), ($), (++), (&&), id, otherwise, error) +import qualified Prelude as P +import Numeric.Units.Dimensional hiding ((*~), (/~), (*), (/), (^), recip, nroot, siUnit) +import qualified Numeric.Units.Dimensional as Dim +import Numeric.Units.Dimensional.Coercion +import Numeric.Units.Dimensional.UnitNames (UnitName, baseUnitName) +import qualified Numeric.Units.Dimensional.UnitNames.InterchangeNames as I +import qualified Numeric.Units.Dimensional.UnitNames as N +import Numeric.Units.Dimensional.Dimensions.TermLevel (HasDynamicDimension(..), DynamicDimension(..), matchDimensions, isCompatibleWith) +import qualified Numeric.Units.Dimensional.Dimensions.TermLevel as D + +-- | The class of types that can be used to model 'Quantity's that are certain to have a value with +-- some dimension. +class Demotable (q :: Type -> Type) where + demotableOut :: q a -> AnyQuantity a + +-- | The class of types that can be used to model 'Quantity's whose 'Dimension's are +-- only known dynamically. +class Promotable (q :: Type -> Type) where + promotableIn :: AnyQuantity a -> q a + promotableOut :: q a -> DynQuantity a + +-- | Forgets information about a 'Quantity' or 'AnyQuantity', yielding an 'AnyQuantity' or a 'DynQuantity'. +demoteQuantity :: (Demotable q, Promotable d) => q a -> d a +demoteQuantity = promotableIn . demotableOut + +-- | Converts a dynamic quantity such as an 'AnyQuantity' or a 'DynQuantity' into a +-- 'Quantity', or to 'Nothing' if the dynamic quantity cannot be represented in the +-- narrower result type. +promoteQuantity :: forall a d q.(Promotable q, KnownDimension d) => q a -> Maybe (Quantity d a) +promoteQuantity = promoteQ . promotableOut + where + dim' = dimension (Proxy :: Proxy d) + promoteQ (DynQuantity d v) | d `isCompatibleWith` dim' = Just . Quantity $ v + | otherwise = Nothing + +instance (KnownDimension d) => Demotable (Quantity d) where + demotableOut q@(Quantity x) = AnyQuantity (dimension q) x + +-- | A 'Quantity' whose 'Dimension' is only known dynamically. +data AnyQuantity a = AnyQuantity !Dimension' !a + deriving (Eq, Data, Generic, Generic1, Typeable) + +instance (Show a) => Show (AnyQuantity a) where + show (AnyQuantity d a) | d == D.dOne = show a + | otherwise = (show a) ++ " " ++ (show . baseUnitName $ d) + +instance HasDynamicDimension (AnyQuantity a) where + +instance HasDimension (AnyQuantity a) where + dimension (AnyQuantity d _) = d + +instance NFData a => NFData (AnyQuantity a) -- instance is derived from Generic instance + +instance Promotable AnyQuantity where + promotableIn = id + promotableOut (AnyQuantity d a) = DynQuantity (SomeDimension d) a + +instance Demotable AnyQuantity where + demotableOut = id + +-- | 'AnyQuantity's form a 'Semigroup' under multiplication, but not under addition because +-- they may not be added together if their dimensions do not match. +instance Num a => Semigroup (AnyQuantity a) where + (AnyQuantity d1 a1) <> (AnyQuantity d2 a2) = AnyQuantity (d1 D.* d2) (a1 P.* a2) + +-- | 'AnyQuantity's form a 'Monoid' under multiplication, but not under addition because +-- they may not be added together if their dimensions do not match. +instance Num a => Monoid (AnyQuantity a) where + mempty = demoteQuantity (1 Dim.*~ one) + mappend = (Data.Semigroup.<>) + +-- | Possibly a 'Quantity' whose 'Dimension' is only known dynamically. +-- +-- By modeling the absence of a value, this type differs from 'AnyQuantity' in that it may +-- not be a 'Quantity' of any 'Dimension' whatsoever, but in exchange it gains instances +-- for the common numeric classes. It's therefore useful for manipulating, and not merely storing, +-- quantities of unknown dimension. +-- +-- This type also contains a 'polydimensionalZero', representing zero value of any dimension. +-- +-- Note that the 'Eq' instance for 'DynQuantity' equates all representations of an invalid value, +-- and also does not equate polydimensional zero with zero of any specific dimension. +data DynQuantity a = DynQuantity !DynamicDimension a -- we can't have strictness annotation on a as it is sometimes undefined + deriving (Data, Generic, Generic1, Typeable) + +instance Eq a => Eq (DynQuantity a) where + (DynQuantity NoDimension _) == (DynQuantity NoDimension _) = True -- all invalid quantities are equal + (DynQuantity NoDimension _) == _ = False -- invalid quanties are not equal to any other quantity + _ == (DynQuantity NoDimension _) = False + (DynQuantity d1 v1) == (DynQuantity d2 v2) = d1 == d2 && v1 == v2 + +instance NFData a => NFData (DynQuantity a) -- instance is derived from Generic instance + +instance Show a => Show (DynQuantity a) where + show (DynQuantity NoDimension _) = "invalidQuantity" + show (DynQuantity AnyDimension v) = show v + show (DynQuantity (SomeDimension d) v) = show $ AnyQuantity d v + +instance Promotable DynQuantity where + promotableIn (AnyQuantity d a) = DynQuantity (SomeDimension d) a + promotableOut = id + +instance HasDynamicDimension (DynQuantity a) where + dynamicDimension (DynQuantity d _) = d + +instance Num a => Num (DynQuantity a) where + x + y = liftDQ2 matchDimensions (P.+) x y + x - y = liftDQ2 matchDimensions (P.-) x y + x * y = liftDQ2 (valid2 (D.*)) (P.*) x y + negate = liftDQ id P.negate + abs = liftDQ id P.abs + signum = liftDQ (constant D.dOne) P.signum + fromInteger = demoteQuantity . (Dim.*~ one) . P.fromInteger + +instance Fractional a => Fractional (DynQuantity a) where + x / y = liftDQ2 (valid2 (D./)) (P./) x y + recip = liftDQ (valid D.recip) P.recip + fromRational = demoteQuantity . (Dim.*~ one) . P.fromRational + +instance Floating a => Floating (DynQuantity a) where + pi = demoteQuantity pi + exp = liftDimensionless P.exp + log = liftDimensionless P.log + sqrt = liftDQ (whenValid $ D.nroot 2) P.sqrt + (**) = liftDQ2 (matchDimensions3 $ SomeDimension D.dOne) (P.**) + logBase = liftDQ2 (matchDimensions3 $ SomeDimension D.dOne) P.logBase + sin = liftDimensionless P.sin + cos = liftDimensionless P.cos + tan = liftDimensionless P.tan + asin = liftDimensionless P.asin + acos = liftDimensionless P.acos + atan = liftDimensionless P.atan + sinh = liftDimensionless P.sinh + cosh = liftDimensionless P.cosh + tanh = liftDimensionless P.tanh + asinh = liftDimensionless P.asinh + acosh = liftDimensionless P.acosh + atanh = liftDimensionless P.atanh + +-- | 'DynQuantity's form a 'Semigroup' under multiplication, but not under addition because +-- they may not be added together if their dimensions do not match. +instance Num a => Semigroup (DynQuantity a) where + (<>) = (P.*) + +-- | 'DynQuantity's form a 'Monoid' under multiplication, but not under addition because +-- they may not be added together if their dimensions do not match. +instance Num a => Monoid (DynQuantity a) where + mempty = demoteQuantity (1 Dim.*~ one) + mappend = (Data.Semigroup.<>) + +-- | A 'DynQuantity' which does not correspond to a value of any dimension. +invalidQuantity :: DynQuantity a +invalidQuantity = DynQuantity NoDimension $ error "Attempt to evaluate the value of an invalid quantity." + +-- | A 'DynQuantity' which corresponds to zero value of any dimension. +-- +-- When combined through arithmetic with other 'DynQuantity's, inference is performed. For example, +-- adding a length to polydimensional zero produces that length. Adding two polydimensional zeros produces another. +-- Taking the sine of a polydimensional zero interprets it as a dimensionless zero and produces a dimensionless result. +-- +-- Note that division by 'polydimensionalZero' produces a polydimensional result, which may be an error or some representation +-- of infinity, as determined by the underlying arithmetic type. This behavior was chosen for consistency with the behavior of division +-- by zero 'DynQuantity's of a specific dimension. +polydimensionalZero :: (Num a) => DynQuantity a +polydimensionalZero = DynQuantity AnyDimension 0 + +-- Lifts a function which is only valid on dimensionless quantities into a function on DynQuantitys. +liftDimensionless :: (a -> a) -> DynQuantity a -> DynQuantity a +liftDimensionless f = liftDQ (matchDimensions $ SomeDimension D.dOne) f + +-- Lifts a function on values into a function on DynQuantitys. +liftDQ :: (DynamicDimension -> DynamicDimension) -- ^ How the function operates on dimensions. + -> (a -> a) -- ^ How the function operates on values. + -> DynQuantity a -> DynQuantity a +liftDQ fd fv (DynQuantity d v) = case fd d of + NoDimension -> invalidQuantity + d' -> DynQuantity d' $ fv v + +-- Lifts a function on values into a function on DynQuantitys. +-- +-- This works by treating polydimensional zeros as dimensionless zeros. If that is not the desired behavior, +-- handle polydimensional zeros first and then call this function. +liftDQ2 :: (DynamicDimension -> DynamicDimension -> DynamicDimension) + -> (a -> a -> a) + -> DynQuantity a -> DynQuantity a -> DynQuantity a +liftDQ2 fd fv (DynQuantity d1 v1) (DynQuantity d2 v2) = case fd d1 d2 of + NoDimension -> invalidQuantity + d' -> DynQuantity d' $ fv v1 v2 + +-- Transforms a dynamic dimension in a way which is always valid +valid :: (Dimension' -> Dimension') -> DynamicDimension -> DynamicDimension +valid _ AnyDimension = AnyDimension +valid f (SomeDimension d) = SomeDimension (f d) +valid _ NoDimension = NoDimension + +whenValid :: (Dimension' -> Maybe Dimension') -> DynamicDimension -> DynamicDimension +whenValid _ AnyDimension = AnyDimension +whenValid f (SomeDimension d) | Just d' <- f d = SomeDimension d' +whenValid _ _ = NoDimension + +constant :: Dimension' -> DynamicDimension -> DynamicDimension +constant d AnyDimension = SomeDimension d +constant d (SomeDimension _) = SomeDimension d +constant _ _ = NoDimension + +-- Transforms two dynamic dimensions in a way which is always valid +valid2 :: (Dimension' -> Dimension' -> Dimension') -> DynamicDimension -> DynamicDimension -> DynamicDimension +valid2 _ AnyDimension (SomeDimension _) = AnyDimension +valid2 _ (SomeDimension _) AnyDimension = AnyDimension +valid2 _ AnyDimension AnyDimension = AnyDimension +valid2 f (SomeDimension d1) (SomeDimension d2) = SomeDimension (f d1 d2) +valid2 _ _ _ = NoDimension + +matchDimensions3 :: DynamicDimension -> DynamicDimension -> DynamicDimension -> DynamicDimension +matchDimensions3 x y z = matchDimensions x (matchDimensions y z) + +-- | A 'Unit' whose 'Dimension' is only known dynamically. +data AnyUnit = AnyUnit Dimension' (UnitName 'NonMetric) ExactPi + deriving (Generic, Typeable) + +instance Show AnyUnit where + show (AnyUnit _ n e) = (show n) ++ " =def= " ++ (show e) ++ " of the SI base unit" + +instance HasDynamicDimension AnyUnit where + +instance HasDimension AnyUnit where + dimension (AnyUnit d _ _) = d + +instance I.HasInterchangeName AnyUnit where + interchangeName (AnyUnit _ n _) = I.interchangeName n + +-- | 'AnyUnit's form a 'Semigroup' under multiplication. +instance Semigroup AnyUnit where + (<>) = (Numeric.Units.Dimensional.Dynamic.*) + +-- | 'AnyUnit's form a 'Monoid' under multiplication. +instance Monoid AnyUnit where + mempty = demoteUnit' one + mappend = (Data.Semigroup.<>) + +anyUnitName :: AnyUnit -> UnitName 'NonMetric +anyUnitName (AnyUnit _ n _) = n + +-- | The dynamic SI coherent unit of a given dimension. +siUnit :: Dimension' -> AnyUnit +siUnit d = AnyUnit d (baseUnitName d) 1 + +-- | Converts a 'Unit' of statically known 'Dimension' into an 'AnyUnit'. +demoteUnit :: forall m d a.(KnownDimension d) => Unit m d a -> AnyUnit +demoteUnit u = AnyUnit dim (name $ weaken u) (exactValue u) + where + dim = dimension (Proxy :: Proxy d) + +-- | Converts a 'Unit' of statically known 'Dimension' into an 'AnyUnit'. +-- +-- This is the same as the more general 'demoteUnit' but is useful in certain circumstances to avoid +-- needlessly introducing an ambiguous type variable. +demoteUnit' :: (KnownDimension d) => Unit m d ExactPi -> AnyUnit +demoteUnit' = demoteUnit + +-- | Converts an 'AnyUnit' into a 'Unit' of statically known 'Dimension', or 'Nothing' if the dimension does not match. +-- +-- The result is represented in 'ExactPi', conversion to other representations is possible using 'changeRepApproximate'. +-- +-- The result is always tagged as 'NonMetric', conversion to a 'Metric' unit can be attempted using 'strengthen'. +promoteUnit :: forall d.(KnownDimension d) => AnyUnit -> Maybe (Unit 'NonMetric d ExactPi) +promoteUnit (AnyUnit dim n e) | dim == dim' = Just $ mkUnitR n e Dim.siUnit + | otherwise = Nothing + where + dim' = dimension (Proxy :: Proxy d) + +-- | Forms the reciprocal of a dynamic unit. +recip :: AnyUnit -> AnyUnit +recip (AnyUnit d n e) = AnyUnit (D.recip d) (N.nOne N./ n) (P.recip e) + +-- | Forms the product of two dynamic units. +(*) :: AnyUnit -> AnyUnit -> AnyUnit +(AnyUnit d1 n1 e1) * (AnyUnit d2 n2 e2) = AnyUnit (d1 D.* d2) (n1 N.* n2) (e1 P.* e2) + +-- | Forms the quotient of two dynamic units. +(/) :: AnyUnit -> AnyUnit -> AnyUnit +(AnyUnit d1 n1 e1) / (AnyUnit d2 n2 e2) = AnyUnit (d1 D./ d2) (n1 N./ n2) (e1 P./ e2) + +-- | Raises a dynamic unit to an integer power. +(^) :: (P.Integral a) => AnyUnit -> a -> AnyUnit +(AnyUnit d n e) ^ x = AnyUnit (d D.^ P.fromIntegral x) (n N.^ P.fromIntegral x) (e P.^^ x) + +-- | Applies a prefix to a dynamic unit. +-- Returns 'Nothing' if the 'Unit' was 'NonMetric' and thus could not accept a prefix. +applyPrefix :: N.Prefix -> AnyUnit -> Maybe AnyUnit +applyPrefix p (AnyUnit d n e) = do + n' <- N.strengthen n + let n'' = N.applyPrefix p n' + let e' = (P.fromRational $ N.scaleFactor p) P.* e + return $ AnyUnit d n'' e' + +-- | Forms a dynamic quantity by multipliying a number and a dynamic unit. +(*~) :: (Floating a, Promotable q) => a -> AnyUnit -> q a +x *~ (AnyUnit d _ e) = promotableIn $ AnyQuantity d (x P.* approximateValue e) + +-- | Divides a dynamic quantity by a dynamic unit, obtaining the numerical value of the quantity +-- expressed in that unit if they are of the same physical dimension, or 'Nothing' otherwise. +(/~) :: (Floating a, Promotable q) => q a -> AnyUnit -> Maybe a +x /~ (AnyUnit d _ e) = case promotableOut x of + DynQuantity d' x' | d' `isCompatibleWith` d -> Just $ x' P./ approximateValue e + | otherwise -> Nothing
src/Numeric/Units/Dimensional/FixedPoint.hs view
@@ -1,373 +1,373 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Experimental- Portability: GHC only?--Defines types for manipulation of quantities with fixed point representations.--}-module Numeric.Units.Dimensional.FixedPoint-(- -- * Types- -- $types- Dimensional,- Unit, Quantity, SQuantity,- Metricality(..),- -- * Physical Dimensions- Dimension (Dim),- -- ** Dimension Arithmetic- type (*), type (/), type (^), NRoot, Recip,- -- ** Term Level Representation of Dimensions- Dimension' (Dim'), HasDimension(..), KnownDimension,- -- * Dimensional Arithmetic- (*~), (/~),- (*), (/), (+), (-),- negate, abs,- -- ** Transcendental Functions- -- *** Via 'Double'- expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD, atan2D,- -- *** Via arbitary 'Floating' type- expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia, atan2Via,- -- ** Operations on Collections- (*~~), (/~~), sum, mean, -- dimensionlessLength, nFromTo,- -- ** Conversion Between Representations- rescale, rescaleFinite, rescaleD, rescaleVia, KnownVariant(dmap), changeRep, changeRepRound, changeRepApproximate,- -- * Dimension Synonyms- DOne, DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity,- -- * Quantity Synonyms- Dimensionless, Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity,- -- * Constants- _0, epsilon,- -- $possibly-imprecise-constants- _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau,- -- * Constructing Units- siUnit, one, mkUnitR, mkUnitQ, mkUnitZ,- -- * Unit Metadata- name, exactValue, weaken, strengthen, exactify,- -- * Commonly Used Type Synonyms- -- $synonyms- type Q, type QScale, type Angle8, type Angle16, type Angle32-)-where--import Data.Bits-import Data.ExactPi-import qualified Data.ExactPi.TypeLevel as E-import Data.Int-import Data.Proxy-import qualified Data.Foldable as F-import Data.Ratio-import qualified GHC.TypeLits as N-import Numeric.Units.Dimensional.Coercion-import Numeric.Units.Dimensional.Internal-import Numeric.Units.Dimensional.Prelude hiding ((*~), (/~), (+), (-), recip, negate, abs, (*~~), (/~~), sum, mean, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau, changeRep)-import Numeric.Units.Dimensional.Variants hiding (type (*), type (/))-import qualified Numeric.Units.Dimensional.UnitNames as Name-import qualified Prelude as P--{- $types--We provide access to the same 'Dimensional', 'Unit', and 'Quantity' types as are exposed by "Numeric.Units.Dimensional", but additionally-offer the 'SQuantity' type to represent scaled quantities. Fixed-point quantities are quantities backed by integers, it is frequently-necessary to scale those integers into a range appropriate for the physical problem at hand.---}--{---Arithmetic Operators and Functions--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.---}----infixr 8 ^, ^/, **-infixl 6 +, ----- | Adds two possibly scaled 'SQuantity's, preserving any scale factor.------ Use in conjunction with 'changeRepRound' to combine quantities with differing scale factors.-(+) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a-(+) = liftQ2 (P.+)---- | Subtracts one possibly scaled 'SQuantity' from another, preserving any scale factor.------ Use in conjunction with 'changeRepRound' to combine quantities with differing scale factors.-(-) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a-(-) = liftQ2 (P.-)---- | Takes the absolute value of a possibly scaled 'SQuantity', preserving any scale factor.-abs :: (Num a) => SQuantity s d a -> SQuantity s d a-abs = liftQ (P.abs)---- | Negates the value of a possibly scaled 'SQuantity', preserving any scale factor.-negate :: (Num a) => SQuantity s d a -> SQuantity s d a-negate = liftQ (P.negate)--infixl 7 *~~, /~~---- | Applies '*~' to all values in a functor.-(*~~) :: (Functor f, RealFrac a, Integral b, E.MinCtxt s a) => f a -> Unit m d a -> f (SQuantity s d b)-xs *~~ u = fmap (*~ u) xs---- | Applies '/~' to all values in a functor.-(/~~) :: (Functor f, Real a, Fractional b, E.MinCtxt s b) => f (SQuantity s d a) -> Unit m d b -> f b-xs /~~ u = fmap (/~ u) xs---- | The sum of all elements in a list.-sum :: (Num a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a-sum = F.foldr (+) _0---- | The arithmetic mean of all elements in a list.-mean :: (Fractional a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a-mean = reduce . F.foldr accumulate (_0, 0 :: Int)- where- reduce (s, n) = dmap (P./ fromIntegral n) s- accumulate val (accum, count) = (accum + val, count P.+ 1)--expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD- :: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne b-expD = expVia (Proxy :: Proxy P.Double)-logD = logVia (Proxy :: Proxy P.Double)-sinD = sinVia (Proxy :: Proxy P.Double)-cosD = cosVia (Proxy :: Proxy P.Double)-tanD = tanVia (Proxy :: Proxy P.Double)-asinD = asinVia (Proxy :: Proxy P.Double)-acosD = acosVia (Proxy :: Proxy P.Double)-atanD = atanVia (Proxy :: Proxy P.Double)-sinhD = sinhVia (Proxy :: Proxy P.Double)-coshD = coshVia (Proxy :: Proxy P.Double)-tanhD = tanhVia (Proxy :: Proxy P.Double)-asinhD = asinhVia (Proxy :: Proxy P.Double)-acoshD = acoshVia (Proxy :: Proxy P.Double)-atanhD = atanhVia (Proxy :: Proxy P.Double)---- | The standard two argument arctangent function.--- Since it interprets its two arguments in comparison with one another, the input may have any dimension.-atan2D :: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double, E.MinCtxt s3 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne a -> SQuantity s3 DOne b-atan2D = atan2Via (Proxy :: Proxy P.Double)--expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia- :: (Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c-expVia = liftDimensionlessVia P.exp-logVia = liftDimensionlessVia P.log-sinVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sin-cosVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cos-tanVia = liftDimensionlessPeriodicVia P.pi P.tan-asinVia = liftDimensionlessVia P.asin-acosVia = liftDimensionlessVia P.acos-atanVia = liftDimensionlessVia P.atan-sinhVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sinh-coshVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cosh-tanhVia = liftDimensionlessPeriodicVia P.pi P.tanh-asinhVia = liftDimensionlessVia P.asinh-acoshVia = liftDimensionlessVia P.acosh-atanhVia = liftDimensionlessVia P.atanh---- | The standard two argument arctangent function.--- Since it interprets its two arguments in comparison with one another, the input may have any dimension.-atan2Via :: forall s1 s2 s3 a b c d.(Integral a, RealFloat b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b, E.MinCtxt s3 b, KnownDimension d) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d a -> SQuantity s3 DOne c-atan2Via _ y x = (*~ siUnit) $ (P.atan2 :: b -> b -> b) (y /~ siUnit) (x /~ siUnit)---- | Lift a function on dimensionless values of a specified intermediate type to operate on possibly scaled dimensionless.-liftDimensionlessVia :: forall s1 s2 a b c.(Real a, RealFrac b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => (b -> b) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c-liftDimensionlessVia f _ = (*~ siUnit) . (f :: b -> b) . (/~ siUnit)---- | Lift a periodic function on dimensionless values of a specified intermediate type to operate on possibly scaled dimensionless.------ If the scale factor of the input type is an exact integer divisor of the function's period, the argument--- will be clamped via an integer `mod` operation prior to applying the function to avoid errors introduced by a floating point modulus.-liftDimensionlessPeriodicVia :: forall s1 s2 a b c.(Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => ExactPi -> (forall d.Floating d => d -> d) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c-liftDimensionlessPeriodicVia p f proxy | Just p'' <- p', p'' /= 0 = (liftDimensionlessVia f proxy) . dmap (`mod` p'')- | otherwise = liftDimensionlessVia f proxy- where- p' :: Maybe a- p' = fmap fromInteger . toExactInteger . P.recip . (P./ p) . E.exactPiVal $ (Proxy :: Proxy s1)--{--We give '*~' and '/~' the same fixity as '*' and '/' defined below.-Note that this necessitates the use of parenthesis when composing-units using '*' and '/', e.g. "1 *~ (meter / second)".--}--infixl 7 *~, /~---- | Forms a possibly scaled 'SQuantity' by multipliying a number and a unit.-(*~) :: forall s m d a b.(RealFrac a, Integral b, E.MinCtxt s a) => a -> Unit m d a -> SQuantity s d b-x *~ (Unit _ _ y) = Quantity . round $ (x P.* y P./ s)- where- s = E.injMin (Proxy :: Proxy s)---- | Divides a possibly scaled 'SQuantity' by a 'Unit' of the same physical dimension, obtaining the--- numerical value of the quantity expressed in that unit.-(/~) :: forall s m d a b.(Real a, Fractional b, E.MinCtxt s b) => SQuantity s d a -> Unit m d b -> b-(Quantity x) /~ (Unit _ _ y) = ((realToFrac x) P.* s P./ y)- where- s = E.injMin (Proxy :: Proxy s)--{---Rescaling Operations---}---- | Rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type.------ Note that this uses an arbitrary precision representation of 'pi', which may be quite slow.-rescale :: forall a b d s1 s2.(Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b-rescale | Just s' <- toExactInteger s = viaInteger (P.* s')- | Just s' <- toExactInteger (P.recip s) = viaInteger (`P.quot` s')- | Just q <- toExactRational s = viaInteger $ timesRational q- | otherwise = viaInteger $ \x -> fixedPoint (fmap (($ x) . timesRational) (rationalApproximations s))- where- s = (s1' P./ s2')- s1' = E.exactPiVal (Proxy :: Proxy s1)- s2' = E.exactPiVal (Proxy :: Proxy s2)- timesRational :: Rational -> Integer -> Integer- timesRational q = (`P.quot` denominator q) . (P.* numerator q)---- | Rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type.------ Expected to outperform `rescale` when a `FiniteBits` context is available for the source and destination representation types.-rescaleFinite :: (Integral a, FiniteBits a, Integral b, FiniteBits b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b-rescaleFinite = rescale -- It should be possible to do this more quickly, since we have a priori knowledge of how well we need to approximate the result---- | Approximately rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type.------ Uses approximate arithmetic by way of an intermediate `Floating` type, to which a proxy must be supplied.-rescaleVia :: forall a b c d s1 s2.(Integral a, RealFrac b, Floating b, Integral c, E.KnownExactPi s1, E.KnownExactPi s2) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d c-rescaleVia _ = viaIntermediate (P.* s)- where- s = approximateValue (s1' P./ s2') :: b- s1' = E.exactPiVal $ (Proxy :: Proxy s1)- s2' = E.exactPiVal $ (Proxy :: Proxy s2)---- | Approximately rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type.------ Uses approximate arithmetic by way of an intermediate `Double` representation.-rescaleD :: (Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b-rescaleD = rescaleVia (Proxy :: Proxy Double)---- Note that this does not respect scaling factors at all.-viaInteger :: (Integral a, Integral b) => (P.Integer -> P.Integer) -> SQuantity s1 d a -> SQuantity s2 d b-viaInteger f = Quantity . fromInteger . f . fromIntegral . unQuantity---- Note that this does not respect scaling factors at all.-viaIntermediate :: (Integral a, RealFrac b, Integral c) => (b -> b) -> SQuantity s1 d a -> SQuantity s2 d c-viaIntermediate f = Quantity . round . f . fromIntegral . unQuantity--fixedPoint :: (Eq a) => [a] -> a-fixedPoint [] = error "Fixed point of empty list."-fixedPoint [x] = x-fixedPoint (x1:x2:xs) | x1 == x2 = x1- | otherwise = fixedPoint (x2:xs)--{---Changes of Representation---}---- | Convenient conversion between numerical types while retaining dimensional information.-changeRep :: forall v1 v2 d a b.- (KnownVariant v1, KnownVariant v2,- CompatibleVariants v1 v2,- E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) b,- Real a, Fractional b)- => Dimensional v1 d a -> Dimensional v2 d b-changeRep = liftD (P.* s) ((P.* s') . realToFrac) Name.weaken- where- p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2)- p = Proxy- s = E.exactPiVal p- s' = E.injMin p---- | Convenient conversion to types with `Integral` representations using `round`.-changeRepRound :: forall v1 v2 d a b.- (KnownVariant v1, KnownVariant v2,- CompatibleVariants v1 v2,- E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) a,- RealFrac a, Integral b)- => Dimensional v1 d a -> Dimensional v2 d b-changeRepRound = liftD (P.* s) (round . (P.* s')) Name.weaken- where- p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2)- p = Proxy- s = E.exactPiVal p- s' = E.injMin p--{---Useful Constant Values---}--{- $possibly-imprecise-constants--Note that, other than '_0' and 'epsilon', these constants may not be exactly representable with certain scale factors.---}---- | The constant for zero is polymorphic, allowing--- it to express zero 'Length' or 'Capacitance' or 'Velocity' etc, in addition--- to the 'Dimensionless' value zero.-_0 :: Num a => SQuantity s d a-_0 = Quantity 0--_1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a-_1 = rescale (epsilon :: SQuantity E.One DOne Integer)-_2 = rescale (epsilon :: SQuantity (E.ExactNatural 2) DOne Integer)-_3 = rescale (epsilon :: SQuantity (E.ExactNatural 3) DOne Integer)-_4 = rescale (epsilon :: SQuantity (E.ExactNatural 4) DOne Integer)-_5 = rescale (epsilon :: SQuantity (E.ExactNatural 5) DOne Integer)-_6 = rescale (epsilon :: SQuantity (E.ExactNatural 6) DOne Integer)-_7 = rescale (epsilon :: SQuantity (E.ExactNatural 7) DOne Integer)-_8 = rescale (epsilon :: SQuantity (E.ExactNatural 8) DOne Integer)-_9 = rescale (epsilon :: SQuantity (E.ExactNatural 9) DOne Integer)--pi :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a-pi = rescale (epsilon :: SQuantity E.Pi DOne Integer)---- | Twice 'pi'.------ For background on 'tau' see http://tauday.com/tau-manifesto (but also--- feel free to review http://www.thepimanifesto.com).-tau :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a-tau = rescale (epsilon :: SQuantity (E.ExactNatural 2 E.* E.Pi) DOne Integer)---- | The least positive representable value in a given fixed-point scaled quantity type.-epsilon :: (Integral a) => SQuantity s d a-epsilon = Quantity 1--{- $synonyms--These type synonyms for commonly used fixed-point types are provided for convenience.---}---- | A binary scale factor.-type QScale n = (E.One E./ (E.ExactNatural (2 N.^ n)))---- | A dimensionless number with `n` fractional bits, using a representation of type `a`.-type Q n a = SQuantity (QScale n) DOne a---- | A single-turn angle represented as a signed 8-bit integer.-type Angle8 = SQuantity (E.Pi E.* (QScale 7)) DPlaneAngle Int8---- | A single-turn angle represented as a signed 16-bit integer.-type Angle16 = SQuantity (E.Pi E.* (QScale 15)) DPlaneAngle Int16---- | A single-turn angle represented as a signed 32-bit integer.-type Angle32 = SQuantity (E.Pi E.* (QScale 31)) DPlaneAngle Int32+{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Experimental + Portability: GHC only? + +Defines types for manipulation of quantities with fixed point representations. +-} +module Numeric.Units.Dimensional.FixedPoint +( + -- * Types + -- $types + Dimensional, + Unit, Quantity, SQuantity, + Metricality(..), + -- * Physical Dimensions + Dimension (Dim), + -- ** Dimension Arithmetic + type (*), type (/), type (^), NRoot, Recip, + -- ** Term Level Representation of Dimensions + Dimension' (Dim'), HasDimension(..), KnownDimension, + -- * Dimensional Arithmetic + (*~), (/~), + (*), (/), (+), (-), + negate, abs, + -- ** Transcendental Functions + -- *** Via 'Double' + expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD, atan2D, + -- *** Via arbitary 'Floating' type + expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia, atan2Via, + -- ** Operations on Collections + (*~~), (/~~), sum, mean, -- dimensionlessLength, nFromTo, + -- ** Conversion Between Representations + rescale, rescaleFinite, rescaleD, rescaleVia, KnownVariant(dmap), changeRep, changeRepRound, changeRepApproximate, + -- * Dimension Synonyms + DOne, DLength, DMass, DTime, DElectricCurrent, DThermodynamicTemperature, DAmountOfSubstance, DLuminousIntensity, + -- * Quantity Synonyms + Dimensionless, Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity, + -- * Constants + _0, epsilon, + -- $possibly-imprecise-constants + _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau, + -- * Constructing Units + siUnit, one, mkUnitR, mkUnitQ, mkUnitZ, + -- * Unit Metadata + name, exactValue, weaken, strengthen, exactify, + -- * Commonly Used Type Synonyms + -- $synonyms + type Q, type QScale, type Angle8, type Angle16, type Angle32 +) +where + +import Data.Bits +import Data.ExactPi +import qualified Data.ExactPi.TypeLevel as E +import Data.Int +import Data.Proxy +import qualified Data.Foldable as F +import Data.Ratio +import qualified GHC.TypeLits as N +import Numeric.Units.Dimensional.Coercion +import Numeric.Units.Dimensional.Internal +import Numeric.Units.Dimensional.Prelude hiding ((*~), (/~), (+), (-), recip, negate, abs, (*~~), (/~~), sum, mean, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, pi, tau, changeRep) +import Numeric.Units.Dimensional.Variants hiding (type (*), type (/)) +import qualified Numeric.Units.Dimensional.UnitNames as Name +import qualified Prelude as P + +{- $types + +We provide access to the same 'Dimensional', 'Unit', and 'Quantity' types as are exposed by "Numeric.Units.Dimensional", but additionally +offer the 'SQuantity' type to represent scaled quantities. Fixed-point quantities are quantities backed by integers, it is frequently +necessary to scale those integers into a range appropriate for the physical problem at hand. + +-} + +{- + +Arithmetic Operators and Functions + +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. + +-} + +--infixr 8 ^, ^/, ** +infixl 6 +, - + +-- | Adds two possibly scaled 'SQuantity's, preserving any scale factor. +-- +-- Use in conjunction with 'changeRepRound' to combine quantities with differing scale factors. +(+) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a +(+) = liftQ2 (P.+) + +-- | Subtracts one possibly scaled 'SQuantity' from another, preserving any scale factor. +-- +-- Use in conjunction with 'changeRepRound' to combine quantities with differing scale factors. +(-) :: (Num a) => SQuantity s d a -> SQuantity s d a -> SQuantity s d a +(-) = liftQ2 (P.-) + +-- | Takes the absolute value of a possibly scaled 'SQuantity', preserving any scale factor. +abs :: (Num a) => SQuantity s d a -> SQuantity s d a +abs = liftQ (P.abs) + +-- | Negates the value of a possibly scaled 'SQuantity', preserving any scale factor. +negate :: (Num a) => SQuantity s d a -> SQuantity s d a +negate = liftQ (P.negate) + +infixl 7 *~~, /~~ + +-- | Applies '*~' to all values in a functor. +(*~~) :: (Functor f, RealFrac a, Integral b, E.MinCtxt s a) => f a -> Unit m d a -> f (SQuantity s d b) +xs *~~ u = fmap (*~ u) xs + +-- | Applies '/~' to all values in a functor. +(/~~) :: (Functor f, Real a, Fractional b, E.MinCtxt s b) => f (SQuantity s d a) -> Unit m d b -> f b +xs /~~ u = fmap (/~ u) xs + +-- | The sum of all elements in a list. +sum :: (Num a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a +sum = F.foldr (+) _0 + +-- | The arithmetic mean of all elements in a list. +mean :: (Fractional a, F.Foldable f) => f (SQuantity s d a) -> SQuantity s d a +mean = reduce . F.foldr accumulate (_0, 0 :: Int) + where + reduce (s, n) = dmap (P./ fromIntegral n) s + accumulate val (accum, count) = (accum + val, count P.+ 1) + +expD, logD, sinD, cosD, tanD, asinD, acosD, atanD, sinhD, coshD, tanhD, asinhD, acoshD, atanhD + :: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne b +expD = expVia (Proxy :: Proxy P.Double) +logD = logVia (Proxy :: Proxy P.Double) +sinD = sinVia (Proxy :: Proxy P.Double) +cosD = cosVia (Proxy :: Proxy P.Double) +tanD = tanVia (Proxy :: Proxy P.Double) +asinD = asinVia (Proxy :: Proxy P.Double) +acosD = acosVia (Proxy :: Proxy P.Double) +atanD = atanVia (Proxy :: Proxy P.Double) +sinhD = sinhVia (Proxy :: Proxy P.Double) +coshD = coshVia (Proxy :: Proxy P.Double) +tanhD = tanhVia (Proxy :: Proxy P.Double) +asinhD = asinhVia (Proxy :: Proxy P.Double) +acoshD = acoshVia (Proxy :: Proxy P.Double) +atanhD = atanhVia (Proxy :: Proxy P.Double) + +-- | The standard two argument arctangent function. +-- Since it interprets its two arguments in comparison with one another, the input may have any dimension. +atan2D :: (Integral a, Integral b, E.MinCtxt s1 Double, E.MinCtxt s2 Double, E.MinCtxt s3 Double) => SQuantity s1 DOne a -> SQuantity s2 DOne a -> SQuantity s3 DOne b +atan2D = atan2Via (Proxy :: Proxy P.Double) + +expVia, logVia, sinVia, cosVia, tanVia, asinVia, acosVia, atanVia, sinhVia, coshVia, tanhVia, asinhVia, acoshVia, atanhVia + :: (Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c +expVia = liftDimensionlessVia P.exp +logVia = liftDimensionlessVia P.log +sinVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sin +cosVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cos +tanVia = liftDimensionlessPeriodicVia P.pi P.tan +asinVia = liftDimensionlessVia P.asin +acosVia = liftDimensionlessVia P.acos +atanVia = liftDimensionlessVia P.atan +sinhVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.sinh +coshVia = liftDimensionlessPeriodicVia (2 P.* P.pi) P.cosh +tanhVia = liftDimensionlessPeriodicVia P.pi P.tanh +asinhVia = liftDimensionlessVia P.asinh +acoshVia = liftDimensionlessVia P.acosh +atanhVia = liftDimensionlessVia P.atanh + +-- | The standard two argument arctangent function. +-- Since it interprets its two arguments in comparison with one another, the input may have any dimension. +atan2Via :: forall s1 s2 s3 a b c d.(Integral a, RealFloat b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b, E.MinCtxt s3 b, KnownDimension d) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d a -> SQuantity s3 DOne c +atan2Via _ y x = (*~ siUnit) $ (P.atan2 :: b -> b -> b) (y /~ siUnit) (x /~ siUnit) + +-- | Lift a function on dimensionless values of a specified intermediate type to operate on possibly scaled dimensionless. +liftDimensionlessVia :: forall s1 s2 a b c.(Real a, RealFrac b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => (b -> b) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c +liftDimensionlessVia f _ = (*~ siUnit) . (f :: b -> b) . (/~ siUnit) + +-- | Lift a periodic function on dimensionless values of a specified intermediate type to operate on possibly scaled dimensionless. +-- +-- If the scale factor of the input type is an exact integer divisor of the function's period, the argument +-- will be clamped via an integer `mod` operation prior to applying the function to avoid errors introduced by a floating point modulus. +liftDimensionlessPeriodicVia :: forall s1 s2 a b c.(Integral a, RealFrac b, Floating b, Integral c, E.MinCtxt s1 b, E.MinCtxt s2 b) => ExactPi -> (forall d.Floating d => d -> d) -> Proxy b -> SQuantity s1 DOne a -> SQuantity s2 DOne c +liftDimensionlessPeriodicVia p f proxy | Just p'' <- p', p'' /= 0 = (liftDimensionlessVia f proxy) . dmap (`mod` p'') + | otherwise = liftDimensionlessVia f proxy + where + p' :: Maybe a + p' = fmap fromInteger . toExactInteger . P.recip . (P./ p) . E.exactPiVal $ (Proxy :: Proxy s1) + +{- +We give '*~' and '/~' the same fixity as '*' and '/' defined below. +Note that this necessitates the use of parenthesis when composing +units using '*' and '/', e.g. "1 *~ (meter / second)". +-} + +infixl 7 *~, /~ + +-- | Forms a possibly scaled 'SQuantity' by multipliying a number and a unit. +(*~) :: forall s m d a b.(RealFrac a, Integral b, E.MinCtxt s a) => a -> Unit m d a -> SQuantity s d b +x *~ (Unit _ _ y) = Quantity . round $ (x P.* y P./ s) + where + s = E.injMin (Proxy :: Proxy s) + +-- | Divides a possibly scaled 'SQuantity' by a 'Unit' of the same physical dimension, obtaining the +-- numerical value of the quantity expressed in that unit. +(/~) :: forall s m d a b.(Real a, Fractional b, E.MinCtxt s b) => SQuantity s d a -> Unit m d b -> b +(Quantity x) /~ (Unit _ _ y) = ((realToFrac x) P.* s P./ y) + where + s = E.injMin (Proxy :: Proxy s) + +{- + +Rescaling Operations + +-} + +-- | Rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type. +-- +-- Note that this uses an arbitrary precision representation of 'pi', which may be quite slow. +rescale :: forall a b d s1 s2.(Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b +rescale | Just s' <- toExactInteger s = viaInteger (P.* s') + | Just s' <- toExactInteger (P.recip s) = viaInteger (`P.quot` s') + | Just q <- toExactRational s = viaInteger $ timesRational q + | otherwise = viaInteger $ \x -> fixedPoint (fmap (($ x) . timesRational) (rationalApproximations s)) + where + s = (s1' P./ s2') + s1' = E.exactPiVal (Proxy :: Proxy s1) + s2' = E.exactPiVal (Proxy :: Proxy s2) + timesRational :: Rational -> Integer -> Integer + timesRational q = (`P.quot` denominator q) . (P.* numerator q) + +-- | Rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type. +-- +-- Expected to outperform `rescale` when a `FiniteBits` context is available for the source and destination representation types. +rescaleFinite :: (Integral a, FiniteBits a, Integral b, FiniteBits b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b +rescaleFinite = rescale -- It should be possible to do this more quickly, since we have a priori knowledge of how well we need to approximate the result + +-- | Approximately rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type. +-- +-- Uses approximate arithmetic by way of an intermediate `Floating` type, to which a proxy must be supplied. +rescaleVia :: forall a b c d s1 s2.(Integral a, RealFrac b, Floating b, Integral c, E.KnownExactPi s1, E.KnownExactPi s2) => Proxy b -> SQuantity s1 d a -> SQuantity s2 d c +rescaleVia _ = viaIntermediate (P.* s) + where + s = approximateValue (s1' P./ s2') :: b + s1' = E.exactPiVal $ (Proxy :: Proxy s1) + s2' = E.exactPiVal $ (Proxy :: Proxy s2) + +-- | Approximately rescales a fixed point quantity, accomodating changes both in its scale factor and its representation type. +-- +-- Uses approximate arithmetic by way of an intermediate `Double` representation. +rescaleD :: (Integral a, Integral b, E.KnownExactPi s1, E.KnownExactPi s2) => SQuantity s1 d a -> SQuantity s2 d b +rescaleD = rescaleVia (Proxy :: Proxy Double) + +-- Note that this does not respect scaling factors at all. +viaInteger :: (Integral a, Integral b) => (P.Integer -> P.Integer) -> SQuantity s1 d a -> SQuantity s2 d b +viaInteger f = Quantity . fromInteger . f . fromIntegral . unQuantity + +-- Note that this does not respect scaling factors at all. +viaIntermediate :: (Integral a, RealFrac b, Integral c) => (b -> b) -> SQuantity s1 d a -> SQuantity s2 d c +viaIntermediate f = Quantity . round . f . fromIntegral . unQuantity + +fixedPoint :: (Eq a) => [a] -> a +fixedPoint [] = error "Fixed point of empty list." +fixedPoint [x] = x +fixedPoint (x1:x2:xs) | x1 == x2 = x1 + | otherwise = fixedPoint (x2:xs) + +{- + +Changes of Representation + +-} + +-- | Convenient conversion between numerical types while retaining dimensional information. +changeRep :: forall v1 v2 d a b. + (KnownVariant v1, KnownVariant v2, + CompatibleVariants v1 v2, + E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) b, + Real a, Fractional b) + => Dimensional v1 d a -> Dimensional v2 d b +changeRep = liftD (P.* s) ((P.* s') . realToFrac) Name.weaken + where + p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2) + p = Proxy + s = E.exactPiVal p + s' = E.injMin p + +-- | Convenient conversion to types with `Integral` representations using `round`. +changeRepRound :: forall v1 v2 d a b. + (KnownVariant v1, KnownVariant v2, + CompatibleVariants v1 v2, + E.MinCtxt (ScaleFactor v1 E./ ScaleFactor v2) a, + RealFrac a, Integral b) + => Dimensional v1 d a -> Dimensional v2 d b +changeRepRound = liftD (P.* s) (round . (P.* s')) Name.weaken + where + p :: Proxy (ScaleFactor v1 E./ ScaleFactor v2) + p = Proxy + s = E.exactPiVal p + s' = E.injMin p + +{- + +Useful Constant Values + +-} + +{- $possibly-imprecise-constants + +Note that, other than '_0' and 'epsilon', these constants may not be exactly representable with certain scale factors. + +-} + +-- | The constant for zero is polymorphic, allowing +-- it to express zero 'Length' or 'Capacitance' or 'Velocity' etc, in addition +-- to the 'Dimensionless' value zero. +_0 :: Num a => SQuantity s d a +_0 = Quantity 0 + +_1, _2, _3, _4, _5, _6, _7, _8, _9 :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a +_1 = rescale (epsilon :: SQuantity E.One DOne Integer) +_2 = rescale (epsilon :: SQuantity (E.ExactNatural 2) DOne Integer) +_3 = rescale (epsilon :: SQuantity (E.ExactNatural 3) DOne Integer) +_4 = rescale (epsilon :: SQuantity (E.ExactNatural 4) DOne Integer) +_5 = rescale (epsilon :: SQuantity (E.ExactNatural 5) DOne Integer) +_6 = rescale (epsilon :: SQuantity (E.ExactNatural 6) DOne Integer) +_7 = rescale (epsilon :: SQuantity (E.ExactNatural 7) DOne Integer) +_8 = rescale (epsilon :: SQuantity (E.ExactNatural 8) DOne Integer) +_9 = rescale (epsilon :: SQuantity (E.ExactNatural 9) DOne Integer) + +pi :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a +pi = rescale (epsilon :: SQuantity E.Pi DOne Integer) + +-- | Twice 'pi'. +-- +-- For background on 'tau' see http://tauday.com/tau-manifesto (but also +-- feel free to review http://www.thepimanifesto.com). +tau :: (Integral a, E.KnownExactPi s) => SQuantity s DOne a +tau = rescale (epsilon :: SQuantity (E.ExactNatural 2 E.* E.Pi) DOne Integer) + +-- | The least positive representable value in a given fixed-point scaled quantity type. +epsilon :: (Integral a) => SQuantity s d a +epsilon = Quantity 1 + +{- $synonyms + +These type synonyms for commonly used fixed-point types are provided for convenience. + +-} + +-- | A binary scale factor. +type QScale n = (E.One E./ (E.ExactNatural (2 N.^ n))) + +-- | A dimensionless number with `n` fractional bits, using a representation of type `a`. +type Q n a = SQuantity (QScale n) DOne a + +-- | A single-turn angle represented as a signed 8-bit integer. +type Angle8 = SQuantity (E.Pi E.* (QScale 7)) DPlaneAngle Int8 + +-- | A single-turn angle represented as a signed 16-bit integer. +type Angle16 = SQuantity (E.Pi E.* (QScale 15)) DPlaneAngle Int16 + +-- | A single-turn angle represented as a signed 32-bit integer. +type Angle32 = SQuantity (E.Pi E.* (QScale 31)) DPlaneAngle Int32
src/Numeric/Units/Dimensional/Float.hs view
@@ -1,178 +1,178 @@-{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable--Defines convenience functions for inspecting and manipulating quantities with 'RealFloat'-floating-point representations.--The dimensionally-typed versions of functions from Patrick Perry's @ieee754@ package-copy that package's API as closely as possible, by permission. In turn they are based on-the @tango@ math library for the D language.---}--{-# LANGUAGE ScopedTypeVariables #-}--module Numeric.Units.Dimensional.Float-(- -- * Lifted Predicates from 'RealFloat'- isDenormalized, isInfinite, isNaN, isNegativeZero- -- * Convenience Functions-, isFiniteNumber, scaleFloat- -- * Lifted Functions from "Numeric.IEEE"- -- ** Values-, infinity, minNormal, maxFinite, epsilon, nan- -- ** Arithmetic-, predIEEE, succIEEE, bisectIEEE, copySign- -- ** NaN with Payload-, nanWithPayload, nanPayload, F.maxNaNPayload- -- ** Comparisons-, identicalIEEE, minNum, maxNum, minNaN, maxNaN-)-where--import Control.Applicative-import Data.Word (Word64)-import Prelude (RealFloat)-import qualified Prelude as P-import Numeric.IEEE (IEEE)-import qualified Numeric.IEEE as F-import Numeric.Units.Dimensional.Internal (liftQ, liftQ2)-import Numeric.Units.Dimensional.Prelude hiding (RealFloat(..))-import Numeric.Units.Dimensional.Coercion---- $setup--- >>> :set -XExtendedDefaultRules--- >>> :set -XNegativeLiterals---- | 'True' if the representation of the argument is too small to be represented in normalized format.-isDenormalized :: RealFloat a => Quantity d a -> Bool-isDenormalized = P.isDenormalized . unQuantity---- | 'True' if the representation of the argument is a number and is not infinite.------ >>> isFiniteNumber (_1 / _0)--- False------ >>> isFiniteNumber (_0 / _0)--- False------ >>> isFiniteNumber (_3 / _2)--- True-isFiniteNumber :: RealFloat a => Quantity d a -> Bool-isFiniteNumber = not . liftA2 (||) isNaN isInfinite---- | 'True' if the representation of the argument is an IEEE infinity or negative infinity.------ >>> isInfinite (_1 / _0)--- True------ >>> isInfinite (42 *~ micro farad)--- False-isInfinite :: RealFloat a => Quantity d a -> Bool-isInfinite = P.isInfinite . unQuantity---- | 'True' if the representation of the argument is an IEEE "not-a-number" (NaN) value.------ >>> isNaN _3--- False------ >>> isNaN (_1 / _0)--- False------ >>> isNaN (asin _4)--- True-isNaN :: RealFloat a => Quantity d a -> Bool-isNaN = P.isNaN . unQuantity---- | 'True' if the representation of the argument is an IEEE negative zero.------ >>> isNegativeZero _0--- False------ >>> isNegativeZero $ (-1e-200 *~ one) * (1e-200 *~ one)--- True-isNegativeZero :: RealFloat a => Quantity d a -> Bool-isNegativeZero = P.isNegativeZero . unQuantity---- | Multiplies a floating-point quantity by an integer power of the radix of the representation type.------ Use 'P.floatRadix' to determine the radix.------ >>> let x = 3 *~ meter--- >>> scaleFloat 3 x--- 24.0 m-scaleFloat :: RealFloat a => Int -> Quantity d a -> Quantity d a-scaleFloat x = Quantity . P.scaleFloat x . unQuantity---- | An infinite floating-point quantity.-infinity :: IEEE a => Quantity d a-infinity = Quantity $ F.infinity---- | The smallest representable positive quantity whose representation is normalized.-minNormal :: IEEE a => Quantity d a-minNormal = Quantity $ F.minNormal---- | The largest representable finite floating-point quantity.-maxFinite :: IEEE a => Quantity d a-maxFinite = Quantity $ F.maxFinite---- | The smallest positive value @x@ such that @_1 + x@ is representable.-epsilon :: IEEE a => Dimensionless a-epsilon = Quantity $ F.epsilon---- | @copySign x y@ returns the quantity @x@ with its sign changed to match that of @y@.-copySign :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a-copySign = liftQ2 F.copySign---- | Return 'True' if two floating-point quantities are /exactly/ (bitwise) equal.-identicalIEEE :: IEEE a => Quantity d a -> Quantity d a -> Bool-identicalIEEE (Quantity x) (Quantity y) = F.identicalIEEE x y---- | Return the next largest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged).-succIEEE :: IEEE a => Quantity d a -> Quantity d a-succIEEE = liftQ F.succIEEE---- | Return the next smallest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged).-predIEEE :: IEEE a => Quantity d a -> Quantity d a-predIEEE = liftQ F.predIEEE---- | Given two floating-point quantities with the same sign, return the quantity whose representation is halfway--- between their representations on the IEEE number line. If the signs of the values differ or either is @NaN@,--- the value is undefined.-bisectIEEE :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a-bisectIEEE (Quantity x) (Quantity y) = Quantity $ F.bisectIEEE x y---- | Default @NaN@ quantity.-nan :: IEEE a => Quantity d a-nan = Quantity $ F.nan---- | Quiet @NaN@ quantity with a positive integer payload.--- Payload must be less than 'maxNaNPayload' of the representation type.------ Beware that while some platforms allow using 0 as a payload, this behavior is not portable.-nanWithPayload :: IEEE a => Word64 -> Quantity d a-nanWithPayload = Quantity . F.nanWithPayload---- | The payload stored in a @NaN@ quantity. Undefined if the argument is not @NaN@.-nanPayload :: IEEE a => Quantity d a -> Word64-nanPayload = F.nanPayload . unQuantity---- | Return the minimum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@.-minNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a-minNum = liftQ2 F.minNum---- | Return the maximum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@.-maxNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a-maxNum = liftQ2 F.maxNum---- | Return the minimum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@.-minNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a-minNaN = liftQ2 F.minNaN---- | Return the maximum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@.-maxNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a-maxNaN = liftQ2 F.maxNaN+{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + +Defines convenience functions for inspecting and manipulating quantities with 'RealFloat' +floating-point representations. + +The dimensionally-typed versions of functions from Patrick Perry's @ieee754@ package +copy that package's API as closely as possible, by permission. In turn they are based on +the @tango@ math library for the D language. + +-} + +{-# LANGUAGE ScopedTypeVariables #-} + +module Numeric.Units.Dimensional.Float +( + -- * Lifted Predicates from 'RealFloat' + isDenormalized, isInfinite, isNaN, isNegativeZero + -- * Convenience Functions +, isFiniteNumber, scaleFloat + -- * Lifted Functions from "Numeric.IEEE" + -- ** Values +, infinity, minNormal, maxFinite, epsilon, nan + -- ** Arithmetic +, predIEEE, succIEEE, bisectIEEE, copySign + -- ** NaN with Payload +, nanWithPayload, nanPayload, F.maxNaNPayload + -- ** Comparisons +, identicalIEEE, minNum, maxNum, minNaN, maxNaN +) +where + +import Control.Applicative +import Data.Word (Word64) +import Prelude (RealFloat) +import qualified Prelude as P +import Numeric.IEEE (IEEE) +import qualified Numeric.IEEE as F +import Numeric.Units.Dimensional.Internal (liftQ, liftQ2) +import Numeric.Units.Dimensional.Prelude hiding (RealFloat(..)) +import Numeric.Units.Dimensional.Coercion + +-- $setup +-- >>> :set -XExtendedDefaultRules +-- >>> :set -XNegativeLiterals + +-- | 'True' if the representation of the argument is too small to be represented in normalized format. +isDenormalized :: RealFloat a => Quantity d a -> Bool +isDenormalized = P.isDenormalized . unQuantity + +-- | 'True' if the representation of the argument is a number and is not infinite. +-- +-- >>> isFiniteNumber (_1 / _0) +-- False +-- +-- >>> isFiniteNumber (_0 / _0) +-- False +-- +-- >>> isFiniteNumber (_3 / _2) +-- True +isFiniteNumber :: RealFloat a => Quantity d a -> Bool +isFiniteNumber = not . liftA2 (||) isNaN isInfinite + +-- | 'True' if the representation of the argument is an IEEE infinity or negative infinity. +-- +-- >>> isInfinite (_1 / _0) +-- True +-- +-- >>> isInfinite (42 *~ micro farad) +-- False +isInfinite :: RealFloat a => Quantity d a -> Bool +isInfinite = P.isInfinite . unQuantity + +-- | 'True' if the representation of the argument is an IEEE "not-a-number" (NaN) value. +-- +-- >>> isNaN _3 +-- False +-- +-- >>> isNaN (_1 / _0) +-- False +-- +-- >>> isNaN (asin _4) +-- True +isNaN :: RealFloat a => Quantity d a -> Bool +isNaN = P.isNaN . unQuantity + +-- | 'True' if the representation of the argument is an IEEE negative zero. +-- +-- >>> isNegativeZero _0 +-- False +-- +-- >>> isNegativeZero $ (-1e-200 *~ one) * (1e-200 *~ one) +-- True +isNegativeZero :: RealFloat a => Quantity d a -> Bool +isNegativeZero = P.isNegativeZero . unQuantity + +-- | Multiplies a floating-point quantity by an integer power of the radix of the representation type. +-- +-- Use 'P.floatRadix' to determine the radix. +-- +-- >>> let x = 3 *~ meter +-- >>> scaleFloat 3 x +-- 24.0 m +scaleFloat :: RealFloat a => Int -> Quantity d a -> Quantity d a +scaleFloat x = Quantity . P.scaleFloat x . unQuantity + +-- | An infinite floating-point quantity. +infinity :: IEEE a => Quantity d a +infinity = Quantity $ F.infinity + +-- | The smallest representable positive quantity whose representation is normalized. +minNormal :: IEEE a => Quantity d a +minNormal = Quantity $ F.minNormal + +-- | The largest representable finite floating-point quantity. +maxFinite :: IEEE a => Quantity d a +maxFinite = Quantity $ F.maxFinite + +-- | The smallest positive value @x@ such that @_1 + x@ is representable. +epsilon :: IEEE a => Dimensionless a +epsilon = Quantity $ F.epsilon + +-- | @copySign x y@ returns the quantity @x@ with its sign changed to match that of @y@. +copySign :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a +copySign = liftQ2 F.copySign + +-- | Return 'True' if two floating-point quantities are /exactly/ (bitwise) equal. +identicalIEEE :: IEEE a => Quantity d a -> Quantity d a -> Bool +identicalIEEE (Quantity x) (Quantity y) = F.identicalIEEE x y + +-- | Return the next largest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged). +succIEEE :: IEEE a => Quantity d a -> Quantity d a +succIEEE = liftQ F.succIEEE + +-- | Return the next smallest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged). +predIEEE :: IEEE a => Quantity d a -> Quantity d a +predIEEE = liftQ F.predIEEE + +-- | Given two floating-point quantities with the same sign, return the quantity whose representation is halfway +-- between their representations on the IEEE number line. If the signs of the values differ or either is @NaN@, +-- the value is undefined. +bisectIEEE :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a +bisectIEEE (Quantity x) (Quantity y) = Quantity $ F.bisectIEEE x y + +-- | Default @NaN@ quantity. +nan :: IEEE a => Quantity d a +nan = Quantity $ F.nan + +-- | Quiet @NaN@ quantity with a positive integer payload. +-- Payload must be less than 'maxNaNPayload' of the representation type. +-- +-- Beware that while some platforms allow using 0 as a payload, this behavior is not portable. +nanWithPayload :: IEEE a => Word64 -> Quantity d a +nanWithPayload = Quantity . F.nanWithPayload + +-- | The payload stored in a @NaN@ quantity. Undefined if the argument is not @NaN@. +nanPayload :: IEEE a => Quantity d a -> Word64 +nanPayload = F.nanPayload . unQuantity + +-- | Return the minimum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@. +minNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +minNum = liftQ2 F.minNum + +-- | Return the maximum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@. +maxNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +maxNum = liftQ2 F.maxNum + +-- | Return the minimum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@. +minNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +minNaN = liftQ2 F.minNaN + +-- | Return the maximum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@. +maxNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +maxNaN = liftQ2 F.maxNaN
src/Numeric/Units/Dimensional/Functor.hs view
@@ -1,41 +1,41 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_HADDOCK show-extensions #-}--{-# LANGUAGE CPP #-}-#if MIN_VERSION_base(4,8,0)--- OverlappingInstances was deprecated by GHC 7.10 in favor of OVERLAPPING pragmas.-#else-{-# LANGUAGE OverlappingInstances #-}-{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-}-#endif--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--Provides a 'Functor' instance for 'Dimensional'.--Note that this instance is dubious, because it allows you to break the dimensional abstraction. See 'dmap' for more information.--Note that, while this instance overlaps with that given for 'Dimensionless', it is confluent with that instance.--Note that this is an orphan instance.--}-module Numeric.Units.Dimensional.Functor where--import Numeric.Units.Dimensional-import Prelude---- | A 'Functor' instance for 'Dimensional'.------ Note that this instance is dubious, because it allows you to break the dimensional abstraction. See 'dmap' for more information.------ Note that, while this instance overlaps with that given for 'Dimensionless', it is confluent with that instance.------ Note that this is an orphan instance.-instance {-# OVERLAPPING #-} (KnownVariant v) => Functor (Dimensional v d) where- fmap = dmap+{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# OPTIONS_HADDOCK show-extensions #-} + +{-# LANGUAGE CPP #-} +#if MIN_VERSION_base(4,8,0) +-- OverlappingInstances was deprecated by GHC 7.10 in favor of OVERLAPPING pragmas. +#else +{-# LANGUAGE OverlappingInstances #-} +{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-} +#endif + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +Provides a 'Functor' instance for 'Dimensional'. + +Note that this instance is dubious, because it allows you to break the dimensional abstraction. See 'dmap' for more information. + +Note that, while this instance overlaps with that given for 'Dimensionless', it is confluent with that instance. + +Note that this is an orphan instance. +-} +module Numeric.Units.Dimensional.Functor where + +import Numeric.Units.Dimensional +import Prelude + +-- | A 'Functor' instance for 'Dimensional'. +-- +-- Note that this instance is dubious, because it allows you to break the dimensional abstraction. See 'dmap' for more information. +-- +-- Note that, while this instance overlaps with that given for 'Dimensionless', it is confluent with that instance. +-- +-- Note that this is an orphan instance. +instance {-# OVERLAPPING #-} (KnownVariant v) => Functor (Dimensional v d) where + fmap = dmap
src/Numeric/Units/Dimensional/Internal.hs view
@@ -1,274 +1,275 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE MultiParamTypeClasses #-} -- for Vector instances only-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances #-}--module Numeric.Units.Dimensional.Internal-(- KnownVariant(..),- Dimensional(..),- type Unit, type Quantity, type SQuantity,- siUnit, showIn,- liftD, liftD2,- liftQ, liftQ2-)-where--import Control.Applicative-import Control.DeepSeq-import Control.Monad (liftM)-import Data.AEq (AEq)-import Data.Coerce (coerce)-import Data.Data-import Data.ExactPi-#if MIN_VERSION_base(4,9,0)-import Data.Functor.Classes (Eq1(..), Ord1(..))-#endif-import qualified Data.ExactPi.TypeLevel as E-import Data.Monoid (Monoid(..))-import Data.Semigroup (Semigroup(..))-import Foreign.Ptr (Ptr, castPtr)-import Foreign.Storable (Storable(..))-import GHC.Generics-import Numeric.Units.Dimensional.Dimensions-import Numeric.Units.Dimensional.Variants-import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen)-import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name-import Numeric.Units.Dimensional.UnitNames.InterchangeNames (HasInterchangeName(..))-import qualified Data.Vector.Generic.Mutable as M-import qualified Data.Vector.Generic as G-import qualified Data.Vector.Unboxed.Base as U-import Prelude- ( Show, Eq(..), Ord, Bounded(..), Num, Fractional, Functor, Real(..)- , String, Maybe(..), Double- , (.), ($), (++), (+), (/)- , show, otherwise, undefined, error, fmap, realToFrac- )-import qualified Prelude as P---- $setup--- >>> :set -XNoImplicitPrelude--- >>> import Numeric.Units.Dimensional.Prelude---- | A unit of measurement.-type Unit (m :: Metricality) = Dimensional ('DUnit m)---- | A dimensional quantity.-type Quantity = SQuantity E.One---- | A dimensional quantity, stored as an 'ExactPi'' multiple of its value in its dimension's SI coherent unit.------ The name is an abbreviation for scaled quantity.-type SQuantity s = Dimensional ('DQuantity s)---- | A KnownVariant is one whose term-level 'Dimensional' values we can represent with an associated data family instance--- and manipulate with certain functions, not all of which are exported from the package.------ Each validly constructed type of kind 'Variant' has a 'KnownVariant' instance.-class KnownVariant (v :: Variant) where- -- | A dimensional value, either a 'Quantity' or a 'Unit', parameterized by its 'Dimension' and representation.- data Dimensional v :: Dimension -> * -> *- -- | A scale factor by which the numerical value of this dimensional value is implicitly multiplied.- type ScaleFactor v :: E.ExactPi'- extractValue :: Dimensional v d a -> (a, Maybe ExactPi)- extractName :: Dimensional v d a -> Maybe (UnitName 'NonMetric)- injectValue :: (Maybe (UnitName 'NonMetric)) -> (a, Maybe ExactPi) -> Dimensional v d a- -- | Maps over the underlying representation of a dimensional value.- -- The caller is responsible for ensuring that the supplied function respects the dimensional abstraction.- -- This means that the function must preserve numerical values, or linearly scale them while preserving the origin.- dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2--deriving instance Typeable Dimensional--instance KnownVariant ('DQuantity s) where- newtype Dimensional ('DQuantity s) d a = Quantity a- deriving (Eq, Ord, AEq, Data, Generic, Generic1-#if MIN_VERSION_base(4,8,0)- , Typeable -- GHC 7.8 doesn't support deriving this instance-#endif- )- type (ScaleFactor ('DQuantity s)) = s- extractValue (Quantity x) = (x, Nothing)- extractName _ = Nothing- injectValue _ (x, _) = Quantity x- dmap = coerce--instance (Typeable m) => KnownVariant ('DUnit m) where- data Dimensional ('DUnit m) d a = Unit !(UnitName m) !ExactPi !a- deriving (Generic, Generic1-#if MIN_VERSION_base(4,8,0)- , Typeable -- GHC 7.8 doesn't support deriving this instance-#endif- )- type (ScaleFactor ('DUnit m)) = E.One- extractValue (Unit _ e x) = (x, Just e)- extractName (Unit n _ _) = Just . Name.weaken $ n- injectValue (Just n) (x, Just e) | Just n' <- relax n = Unit n' e x- | otherwise = error "Shouldn't be reachable. Needed a metric name but got a non-metric one."- injectValue _ _ = error "Shouldn't be reachable. Needed to name a quantity."- dmap f (Unit n e x) = Unit n e (f x)---- GHC is somewhat unclear about why, but it won't derive this instance, so we give it explicitly.-instance (Bounded a) => Bounded (SQuantity s d a) where- minBound = Quantity minBound- maxBound = Quantity maxBound--#if MIN_VERSION_base(4,9,0)-instance Eq1 (SQuantity s d) where- liftEq = coerce--instance Ord1 (SQuantity s d) where- liftCompare = coerce-#endif--instance HasInterchangeName (Unit m d a) where- interchangeName (Unit n _ _) = interchangeName n--{--Since quantities form a monoid under addition, but not under multiplication unless they are dimensionless,-we will define a monoid instance that adds.--}---- | 'Quantity's of a given 'Dimension' form a 'Semigroup' under addition.-instance (Num a) => Semigroup (SQuantity s d a) where- (<>) = liftQ2 (+)---- | 'Quantity's of a given 'Dimension' form a 'Monoid' under addition.-instance (Num a) => Monoid (SQuantity s d a) where- mempty = Quantity 0- mappend = liftQ2 (+)--{---= Dimensionless =--For dimensionless quantities pretty much any operation is applicable.-We provide this freedom by making 'Dimensionless' an instance of-'Functor'.--}--instance Functor (SQuantity s DOne) where- fmap = dmap--instance (KnownDimension d) => HasDynamicDimension (Dimensional v d a) where--instance (KnownDimension d) => HasDimension (Dimensional v d a) where- dimension _ = dimension (Proxy :: Proxy d)---- | A polymorphic 'Unit' which can be used in place of the coherent--- SI base unit of any dimension. This allows polymorphic quantity--- creation and destruction without exposing the 'Dimensional' constructor.-siUnit :: forall d a.(KnownDimension d, Num a) => Unit 'NonMetric d a-siUnit = Unit (baseUnitName $ dimension (Proxy :: Proxy d)) 1 1--instance NFData a => NFData (Quantity d a) -- instance is derived from Generic instance--instance Storable a => Storable (SQuantity s d a) where- sizeOf _ = sizeOf (undefined::a)- {-# INLINE sizeOf #-}- alignment _ = alignment (undefined::a)- {-# INLINE alignment #-}- poke ptr = poke (castPtr ptr :: Ptr a) . coerce- {-# INLINE poke #-}- peek ptr = liftM Quantity (peek (castPtr ptr :: Ptr a))- {-# INLINE peek #-}--{--Instances for vectors of quantities.--}-newtype instance U.Vector (SQuantity s d a) = V_Quantity {unVQ :: U.Vector a}-newtype instance U.MVector v (SQuantity s d a) = MV_Quantity {unMVQ :: U.MVector v a}-instance U.Unbox a => U.Unbox (SQuantity s d a)--instance (M.MVector U.MVector a) => M.MVector U.MVector (SQuantity s d a) where- basicLength = M.basicLength . unMVQ- {-# INLINE basicLength #-}- basicUnsafeSlice m n = MV_Quantity . M.basicUnsafeSlice m n . unMVQ- {-# INLINE basicUnsafeSlice #-}- basicOverlaps u v = M.basicOverlaps (unMVQ u) (unMVQ v)- {-# INLINE basicOverlaps #-}- basicUnsafeNew = liftM MV_Quantity . M.basicUnsafeNew- {-# INLINE basicUnsafeNew #-}- basicUnsafeRead v = liftM Quantity . M.basicUnsafeRead (unMVQ v)- {-# INLINE basicUnsafeRead #-}- basicUnsafeWrite v i = M.basicUnsafeWrite (unMVQ v) i . coerce- {-# INLINE basicUnsafeWrite #-}-#if MIN_VERSION_vector(0,11,0)- basicInitialize = M.basicInitialize . unMVQ- {-# INLINE basicInitialize #-}-#endif--instance (G.Vector U.Vector a) => G.Vector U.Vector (SQuantity s d a) where- basicUnsafeFreeze = liftM V_Quantity . G.basicUnsafeFreeze . unMVQ- {-# INLINE basicUnsafeFreeze #-}- basicUnsafeThaw = liftM MV_Quantity . G.basicUnsafeThaw . unVQ- {-# INLINE basicUnsafeThaw #-}- basicLength = G.basicLength . unVQ- {-# INLINE basicLength #-}- basicUnsafeSlice m n = V_Quantity . G.basicUnsafeSlice m n . unVQ- {-# INLINE basicUnsafeSlice #-}- basicUnsafeIndexM v = liftM Quantity . G.basicUnsafeIndexM (unVQ v)- {-# INLINE basicUnsafeIndexM #-}--{--We will conclude by providing a reasonable 'Show' instance for-quantities. The SI unit of the quantity is inferred-from its dimension.--}-instance (KnownDimension d, E.KnownExactPi s, Show a, Real a) => Show (SQuantity s d a) where- show (Quantity x) | isExactOne s' = show x ++ showName n- | otherwise = "Quantity " ++ show x ++ " {- " ++ show q ++ " -}"- where- s' = E.exactPiVal (Proxy :: Proxy s)- s'' = approximateValue s' :: Double- q = Quantity (realToFrac x P.* s'') :: Quantity d Double- (Unit n _ _) = siUnit :: Unit 'NonMetric d a---- | Shows the value of a 'Quantity' expressed in a specified 'Unit' of the same 'Dimension'.------ >>> showIn watt $ (37 *~ volt) * (4 *~ ampere)--- "148.0 W"-showIn :: (Show a, Fractional a) => Unit m d a -> Quantity d a -> String-showIn (Unit n _ y) (Quantity x) = show (x / y) ++ (showName . Name.weaken $ n)--showName :: UnitName 'NonMetric -> String-showName n | n == nOne = ""- | otherwise = " " ++ show n--instance (Show a) => Show (Unit m d a) where- show (Unit n e x) = "The unit " ++ show n ++ ", with value " ++ show e ++ " (or " ++ show x ++ ")"---- Operates on a dimensional value using a unary operation on values, possibly yielding a Unit.-liftD :: (KnownVariant v1, KnownVariant v2) => (ExactPi -> ExactPi) -> (a -> b) -> UnitNameTransformer -> (Dimensional v1 d1 a) -> (Dimensional v2 d2 b)-liftD fe f nt x = let (x', e') = extractValue x- n = extractName x- n' = (liftA nt) n- in injectValue n' (f x', fmap fe e')---- Operates on a dimensional value using a unary operation on values, yielding a Quantity.-liftQ :: (a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a-liftQ = coerce---- Combines two dimensional values using a binary operation on values, possibly yielding a Unit.-liftD2 :: (KnownVariant v1, KnownVariant v2, KnownVariant v3) => (ExactPi -> ExactPi -> ExactPi) -> (a -> a -> a) -> UnitNameTransformer2 -> Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional v3 d3 a-liftD2 fe f nt x1 x2 = let (x1', e1') = extractValue x1- (x2', e2') = extractValue x2- n1 = extractName x1- n2 = extractName x2- n' = (liftA2 nt) n1 n2- in injectValue n' (f x1' x2', fe <$> e1' <*> e2')---- Combines two dimensional values using a binary operation on values, yielding a Quantity.-liftQ2 :: (a -> a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a-liftQ2 = coerce+{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} -- for Vector instances only +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeSynonymInstances #-} + +module Numeric.Units.Dimensional.Internal +( + KnownVariant(..), + Dimensional(..), + type Unit, type Quantity, type SQuantity, + siUnit, showIn, + liftD, liftD2, + liftQ, liftQ2 +) +where + +import Control.Applicative +import Control.DeepSeq +import Control.Monad (liftM) +import Data.AEq (AEq) +import Data.Coerce (coerce) +import Data.Data +import Data.Kind +import Data.ExactPi +#if MIN_VERSION_base(4,9,0) +import Data.Functor.Classes (Eq1(..), Ord1(..)) +#endif +import qualified Data.ExactPi.TypeLevel as E +import Data.Monoid (Monoid(..)) +import Data.Semigroup (Semigroup(..)) +import Foreign.Ptr (Ptr, castPtr) +import Foreign.Storable (Storable(..)) +import GHC.Generics +import Numeric.Units.Dimensional.Dimensions +import Numeric.Units.Dimensional.Variants +import Numeric.Units.Dimensional.UnitNames hiding ((*), (/), (^), weaken, strengthen) +import qualified Numeric.Units.Dimensional.UnitNames.Internal as Name +import Numeric.Units.Dimensional.UnitNames.InterchangeNames (HasInterchangeName(..)) +import qualified Data.Vector.Generic.Mutable as M +import qualified Data.Vector.Generic as G +import qualified Data.Vector.Unboxed.Base as U +import Prelude + ( Show, Eq(..), Ord, Bounded(..), Num, Fractional, Functor, Real(..) + , String, Maybe(..), Double + , (.), ($), (++), (+), (/) + , show, otherwise, undefined, error, fmap, realToFrac + ) +import qualified Prelude as P + +-- $setup +-- >>> :set -XNoImplicitPrelude +-- >>> import Numeric.Units.Dimensional.Prelude + +-- | A unit of measurement. +type Unit (m :: Metricality) = Dimensional ('DUnit m) + +-- | A dimensional quantity. +type Quantity = SQuantity E.One + +-- | A dimensional quantity, stored as an 'ExactPi'' multiple of its value in its dimension's SI coherent unit. +-- +-- The name is an abbreviation for scaled quantity. +type SQuantity s = Dimensional ('DQuantity s) + +-- | A KnownVariant is one whose term-level 'Dimensional' values we can represent with an associated data family instance +-- and manipulate with certain functions, not all of which are exported from the package. +-- +-- Each validly constructed type of kind 'Variant' has a 'KnownVariant' instance. +class KnownVariant (v :: Variant) where + -- | A dimensional value, either a 'Quantity' or a 'Unit', parameterized by its 'Dimension' and representation. + data Dimensional v :: Dimension -> Type -> Type + -- | A scale factor by which the numerical value of this dimensional value is implicitly multiplied. + type ScaleFactor v :: E.ExactPi' + extractValue :: Dimensional v d a -> (a, Maybe ExactPi) + extractName :: Dimensional v d a -> Maybe (UnitName 'NonMetric) + injectValue :: (Maybe (UnitName 'NonMetric)) -> (a, Maybe ExactPi) -> Dimensional v d a + -- | Maps over the underlying representation of a dimensional value. + -- The caller is responsible for ensuring that the supplied function respects the dimensional abstraction. + -- This means that the function must preserve numerical values, or linearly scale them while preserving the origin. + dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2 + +deriving instance Typeable Dimensional + +instance KnownVariant ('DQuantity s) where + newtype Dimensional ('DQuantity s) d a = Quantity a + deriving (Eq, Ord, AEq, Data, Generic, Generic1 +#if MIN_VERSION_base(4,8,0) + , Typeable -- GHC 7.8 doesn't support deriving this instance +#endif + ) + type (ScaleFactor ('DQuantity s)) = s + extractValue (Quantity x) = (x, Nothing) + extractName _ = Nothing + injectValue _ (x, _) = Quantity x + dmap = coerce + +instance (Typeable m) => KnownVariant ('DUnit m) where + data Dimensional ('DUnit m) d a = Unit !(UnitName m) !ExactPi !a + deriving (Generic, Generic1 +#if MIN_VERSION_base(4,8,0) + , Typeable -- GHC 7.8 doesn't support deriving this instance +#endif + ) + type (ScaleFactor ('DUnit m)) = E.One + extractValue (Unit _ e x) = (x, Just e) + extractName (Unit n _ _) = Just . Name.weaken $ n + injectValue (Just n) (x, Just e) | Just n' <- relax n = Unit n' e x + | otherwise = error "Shouldn't be reachable. Needed a metric name but got a non-metric one." + injectValue _ _ = error "Shouldn't be reachable. Needed to name a quantity." + dmap f (Unit n e x) = Unit n e (f x) + +-- GHC is somewhat unclear about why, but it won't derive this instance, so we give it explicitly. +instance (Bounded a) => Bounded (SQuantity s d a) where + minBound = Quantity minBound + maxBound = Quantity maxBound + +#if MIN_VERSION_base(4,9,0) +instance Eq1 (SQuantity s d) where + liftEq = coerce + +instance Ord1 (SQuantity s d) where + liftCompare = coerce +#endif + +instance HasInterchangeName (Unit m d a) where + interchangeName (Unit n _ _) = interchangeName n + +{- +Since quantities form a monoid under addition, but not under multiplication unless they are dimensionless, +we will define a monoid instance that adds. +-} + +-- | 'Quantity's of a given 'Dimension' form a 'Semigroup' under addition. +instance (Num a) => Semigroup (SQuantity s d a) where + (<>) = liftQ2 (+) + +-- | 'Quantity's of a given 'Dimension' form a 'Monoid' under addition. +instance (Num a) => Monoid (SQuantity s d a) where + mempty = Quantity 0 + mappend = liftQ2 (+) + +{- + += Dimensionless = + +For dimensionless quantities pretty much any operation is applicable. +We provide this freedom by making 'Dimensionless' an instance of +'Functor'. +-} + +instance Functor (SQuantity s DOne) where + fmap = dmap + +instance (KnownDimension d) => HasDynamicDimension (Dimensional v d a) where + +instance (KnownDimension d) => HasDimension (Dimensional v d a) where + dimension _ = dimension (Proxy :: Proxy d) + +-- | A polymorphic 'Unit' which can be used in place of the coherent +-- SI base unit of any dimension. This allows polymorphic quantity +-- creation and destruction without exposing the 'Dimensional' constructor. +siUnit :: forall d a.(KnownDimension d, Num a) => Unit 'NonMetric d a +siUnit = Unit (baseUnitName $ dimension (Proxy :: Proxy d)) 1 1 + +instance NFData a => NFData (Quantity d a) -- instance is derived from Generic instance + +instance Storable a => Storable (SQuantity s d a) where + sizeOf _ = sizeOf (undefined::a) + {-# INLINE sizeOf #-} + alignment _ = alignment (undefined::a) + {-# INLINE alignment #-} + poke ptr = poke (castPtr ptr :: Ptr a) . coerce + {-# INLINE poke #-} + peek ptr = liftM Quantity (peek (castPtr ptr :: Ptr a)) + {-# INLINE peek #-} + +{- +Instances for vectors of quantities. +-} +newtype instance U.Vector (SQuantity s d a) = V_Quantity {unVQ :: U.Vector a} +newtype instance U.MVector v (SQuantity s d a) = MV_Quantity {unMVQ :: U.MVector v a} +instance U.Unbox a => U.Unbox (SQuantity s d a) + +instance (M.MVector U.MVector a) => M.MVector U.MVector (SQuantity s d a) where + basicLength = M.basicLength . unMVQ + {-# INLINE basicLength #-} + basicUnsafeSlice m n = MV_Quantity . M.basicUnsafeSlice m n . unMVQ + {-# INLINE basicUnsafeSlice #-} + basicOverlaps u v = M.basicOverlaps (unMVQ u) (unMVQ v) + {-# INLINE basicOverlaps #-} + basicUnsafeNew = liftM MV_Quantity . M.basicUnsafeNew + {-# INLINE basicUnsafeNew #-} + basicUnsafeRead v = liftM Quantity . M.basicUnsafeRead (unMVQ v) + {-# INLINE basicUnsafeRead #-} + basicUnsafeWrite v i = M.basicUnsafeWrite (unMVQ v) i . coerce + {-# INLINE basicUnsafeWrite #-} +#if MIN_VERSION_vector(0,11,0) + basicInitialize = M.basicInitialize . unMVQ + {-# INLINE basicInitialize #-} +#endif + +instance (G.Vector U.Vector a) => G.Vector U.Vector (SQuantity s d a) where + basicUnsafeFreeze = liftM V_Quantity . G.basicUnsafeFreeze . unMVQ + {-# INLINE basicUnsafeFreeze #-} + basicUnsafeThaw = liftM MV_Quantity . G.basicUnsafeThaw . unVQ + {-# INLINE basicUnsafeThaw #-} + basicLength = G.basicLength . unVQ + {-# INLINE basicLength #-} + basicUnsafeSlice m n = V_Quantity . G.basicUnsafeSlice m n . unVQ + {-# INLINE basicUnsafeSlice #-} + basicUnsafeIndexM v = liftM Quantity . G.basicUnsafeIndexM (unVQ v) + {-# INLINE basicUnsafeIndexM #-} + +{- +We will conclude by providing a reasonable 'Show' instance for +quantities. The SI unit of the quantity is inferred +from its dimension. +-} +instance (KnownDimension d, E.KnownExactPi s, Show a, Real a) => Show (SQuantity s d a) where + show (Quantity x) | isExactOne s' = show x ++ showName n + | otherwise = "Quantity " ++ show x ++ " {- " ++ show q ++ " -}" + where + s' = E.exactPiVal (Proxy :: Proxy s) + s'' = approximateValue s' :: Double + q = Quantity (realToFrac x P.* s'') :: Quantity d Double + (Unit n _ _) = siUnit :: Unit 'NonMetric d a + +-- | Shows the value of a 'Quantity' expressed in a specified 'Unit' of the same 'Dimension'. +-- +-- >>> showIn watt $ (37 *~ volt) * (4 *~ ampere) +-- "148.0 W" +showIn :: (Show a, Fractional a) => Unit m d a -> Quantity d a -> String +showIn (Unit n _ y) (Quantity x) = show (x / y) ++ (showName . Name.weaken $ n) + +showName :: UnitName 'NonMetric -> String +showName n | n == nOne = "" + | otherwise = " " ++ show n + +instance (Show a) => Show (Unit m d a) where + show (Unit n e x) = "The unit " ++ show n ++ ", with value " ++ show e ++ " (or " ++ show x ++ ")" + +-- Operates on a dimensional value using a unary operation on values, possibly yielding a Unit. +liftD :: (KnownVariant v1, KnownVariant v2) => (ExactPi -> ExactPi) -> (a -> b) -> UnitNameTransformer -> (Dimensional v1 d1 a) -> (Dimensional v2 d2 b) +liftD fe f nt x = let (x', e') = extractValue x + n = extractName x + n' = (liftA nt) n + in injectValue n' (f x', fmap fe e') + +-- Operates on a dimensional value using a unary operation on values, yielding a Quantity. +liftQ :: (a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a +liftQ = coerce + +-- Combines two dimensional values using a binary operation on values, possibly yielding a Unit. +liftD2 :: (KnownVariant v1, KnownVariant v2, KnownVariant v3) => (ExactPi -> ExactPi -> ExactPi) -> (a -> a -> a) -> UnitNameTransformer2 -> Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional v3 d3 a +liftD2 fe f nt x1 x2 = let (x1', e1') = extractValue x1 + (x2', e2') = extractValue x2 + n1 = extractName x1 + n2 = extractName x2 + n' = (liftA2 nt) n1 n2 + in injectValue n' (f x1' x2', fe <$> e1' <*> e2') + +-- Combines two dimensional values using a binary operation on values, yielding a Quantity. +liftQ2 :: (a -> a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a +liftQ2 = coerce
src/Numeric/Units/Dimensional/NonSI.hs view
@@ -1,891 +1,891 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE NumDecimals #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--= Summary--This module defines units that are not part of the SI, with the-exception of those defined in the "Numeric.Units.Dimensional.SIUnits" module (units outside-of the SI accepted for use with the SI).--Any chapters, sections or tables referenced are from <#note1 [1]> unless-otherwise specified.--== Neper, bel, shannon and the like--The units of section 5.1.2 are purposefully (but not permanently)-omitted. In fact the logarithmic units (see section 8.7) are-problematic and it is not clear how to implement them. Perhaps with-a conversion function similar to for degrees Celsius.--= References--1. #note1# http://physics.nist.gov/Pubs/SP811/-2. #note2# http://www.iau.org/science/publications/proceedings_rules/units/-3. #note3# http://en.m.wikipedia.org/wiki/Pressure-4. #note4# http://en.m.wikipedia.org/wiki/Torr---}--module Numeric.Units.Dimensional.NonSI-(- -- * Units Defined By Experiment- -- $values-obtained-experimentally- electronVolt, unifiedAtomicMassUnit, dalton,- -- * Standard Gravity- gee,- -- * Inch-pound Units- -- $inch-pound-units- poundMass, ounce, poundForce, horsepower, btu, shortTon,- nauticalMile, knot,- revolution, solid,- slug, psi,- teaspoon,- -- ** International Foot- foot, inch, mil, yard, mile, acre,- -- ** US Survey Foot- usSurveyFoot, usSurveyInch, usSurveyMil, usSurveyYard, usSurveyMile, usSurveyAcre,- -- * Years- -- $year- year, century,- -- * Pressure Units- -- $pressure-units- bar, atmosphere, technicalAtmosphere, mmHg, inHg, inHg_UCUM, inHg_NIST, torr,- -- * Radiation Units- rad,- -- * Kinematic Viscosity- stokes,- -- * Temperature- -- $temperature- degreeFahrenheit, degreeRankine,- -- * Imperial Volumes- -- $imperial-volumes- imperialGallon, imperialQuart, imperialPint, imperialCup, imperialGill, imperialFluidOunce,- -- * US Customary Volumes- -- $us-customary-volumes- usGallon, usQuart, usPint, usCup, usGill, usFluidOunce,- -- * Atomic-Scale Units- angstrom,- -- * Units from the Centimeter-Gram-Second Electrostatic System of Units- gauss-)-where--import Numeric.Units.Dimensional.Prelude-import Numeric.Units.Dimensional.UnitNames.Internal (ucumMetric, ucum, dimensionalAtom)-import qualified Prelude---- $setup--- >>> import Data.ExactPi--- >>> import Data.Function (on)--- >>> import Numeric.Units.Dimensional.Coercion--- >>> default (Double)--- >>> :{--- >>> let infix 4 ===--- >>> (===) = areExactlyEqual `on` unQuantity :: Quantity d ExactPi -> Quantity d ExactPi -> Bool--- >>> :}--{- $values-obtained-experimentally--From Table 7, units accepted for use with the SI whose values in SI units are-obtained experimentally.--When <#note1 [1]> was published the electron volt had a standard combined-uncertainity of 0.00000049e-19 J and the unified atomic mass unit-had a combined uncertainty of 0.0000010e-27 kg.---}--electronVolt :: Floating a => Unit 'Metric DEnergy a-electronVolt = mkUnitR (ucumMetric "eV" "eV" "electron volt") 1.60217733e-19 $ joule--unifiedAtomicMassUnit :: Floating a => Unit 'Metric DMass a-unifiedAtomicMassUnit = mkUnitR (ucumMetric "u" "u" "atomic mass unit") 1.6605402e-27 $ kilo gram--dalton :: Floating a => Unit 'Metric DMass a-dalton = mkUnitR (ucumMetric "u" "Da" "Dalton") 1 $ unifiedAtomicMassUnit---- | One gee is the standard value of the acceleration due to gravity at the--- Earth's surface, as standardized by CIPM.------ Note that local values of acceleration due to gravity will differ from the--- standard gravity.------ See <https://en.wikipedia.org/wiki/Standard_gravity here> for further information.------ >>> 1 *~ gee--- 9.80665 m s^-2------ >>> 1 *~ gee :: Acceleration Rational--- 196133 % 20000 m s^-2-gee :: Fractional a => Unit 'Metric DAcceleration a-gee = mkUnitQ (ucumMetric "[g]" "g" "gee") 9.80665 $ meter / second ^ pos2--{- $inch-pound-units-Some US customary (that is, inch-pound) units.--}---- | One international foot is one third of an international 'yard'.------ See <https://en.wikipedia.org/wiki/Foot_%28unit%29#International_foot here> for further information.------ >>> 1 *~ foot--- 0.3048 m------ prop> 3 *~ foot === 1 *~ yard------ >>> 1 *~ foot :: Length Rational--- 381 % 1250 m-foot :: Fractional a => Unit 'NonMetric DLength a-foot = mkUnitQ (ucum "[ft_i]" "ft" "foot") (1 Prelude./ 3) $ yard---- | One inch is one twelth of a 'foot'.------ This inch is based on the international 'foot'.------ See <https://en.wikipedia.org/wiki/Inch#Modern_standardisation here> for further information.------ >>> 1 *~ inch--- 2.54e-2 m------ prop> 12 *~ inch === 1 *~ foot------ >>> 1 *~ inch :: Length Rational--- 127 % 5000 m-inch :: Fractional a => Unit 'NonMetric DLength a-inch = mkUnitQ (ucum "[in_i]" "in" "inch") (1 Prelude./ 12) $ foot---- | One mil is one thousandth of an 'inch'.------ This mil is based on the international 'inch'.------ See <https://en.wikipedia.org/wiki/Thousandth_of_an_inch here> for further information.------ >>> 1 *~ mil--- 2.54e-5 m------ prop> 1000 *~ mil === 1 *~ inch------ >>> 1 *~ mil :: Length Rational--- 127 % 5000000 m-mil :: Fractional a => Unit 'NonMetric DLength a-mil = mkUnitQ (ucum "[mil_i]" "mil" "mil") 0.001 $ inch---- | One yard, as defined by international agreement in 1959, is precisely--- 0.9144 'meter'.------ See <https://en.wikipedia.org/wiki/Yard here> for further information.------ >>> 1 *~ yard--- 0.9144 m------ >>> 1 *~ yard :: Length Rational--- 1143 % 1250 m-yard :: (Fractional a) => Unit 'NonMetric DLength a-yard = mkUnitQ (ucum "[yd_i]" "yd" "yard") 0.9144 $ meter---- | One mile is 5 280 feet.------ This mile is based on the international 'foot'.------ See <https://en.wikipedia.org/wiki/Mile#International_mile here> for further information.------ >>> 1 *~ mile--- 1609.344 m------ prop> 1 *~ mile === 5280 *~ foot------ >>> 1 *~ mile :: Length Rational--- 201168 % 125 m-mile :: (Fractional a) => Unit 'NonMetric DLength a-mile = mkUnitQ (ucum "[mi_i]" "mi" "mile") 5280 $ foot---- | One acre is 43 560 square feet.------ This acre is based on the international 'foot'. For the acre based on the US Survey Foot,--- see 'usSurveyAcre'. While both acres are in use, the difference between them is of little consequence--- for most applications in which either is used.------ See <https://en.wikipedia.org/wiki/Acre#Differences_between_international_and_US_survey_acres here> for further information.------ >>> 1 *~ acre--- 4046.8564224 m^2------ prop> 1 *~ acre === 43560 *~ foot ^ pos2------ >>> 1 *~ acre :: Area Rational--- 316160658 % 78125 m^2-acre :: (Fractional a) => Unit 'NonMetric DArea a-acre = mkUnitQ (dimensionalAtom "[acr_i]" "ac" "acre") 43560 $ square foot---- | One US survey foot is 1200/3937 'meter'.------ For the international foot, see 'foot'. Note that this is not the foot in routine use--- in the United States.------ See <https://en.wikipedia.org/wiki/Foot_%28unit%29#US_survey_foot here> for further information.------ >>> 1 *~ usSurveyFoot--- 0.3048006096012192 m------ >>> 1 *~ usSurveyFoot :: Length Rational--- 1200 % 3937 m-usSurveyFoot :: Fractional a => Unit 'NonMetric DLength a-usSurveyFoot = mkUnitQ (ucum "[ft_us]" "ft" "foot") (1200 Prelude./ 3937) $ meter---- | One inch is one twelth of a foot.------ This inch is based on the 'usSurveyFoot'. For the inch based on the international foot,--- see 'inch'. Note that this is not the inch in routine use in the United States.------ See <https://en.wikipedia.org/wiki/Inch here> for further information.------ >>> 1 *~ usSurveyInch--- 2.54000508001016e-2 m------ prop> 12 *~ usSurveyInch === 1 *~ usSurveyFoot------ >>> 1 *~ usSurveyInch :: Length Rational--- 100 % 3937 m-usSurveyInch :: Fractional a => Unit 'NonMetric DLength a-usSurveyInch = mkUnitQ (ucum "[in_us]" "in" "inch") (1 Prelude./ 12) $ usSurveyFoot---- | One mil is one thousandth of an inch.------ This mil is based on the 'usSurveyInch'. For the mil based on the international inch,--- see 'mil'. Note that this is not the mil in routine use in the United States.------ See <https://en.wikipedia.org/wiki/Thousandth_of_an_inch here> for further information.------ >>> 1 *~ usSurveyMil--- 2.54000508001016e-5 m------ prop> 1000 *~ usSurveyMil === 1 *~ usSurveyInch------ >>> 1 *~ usSurveyMil :: Length Rational--- 1 % 39370 m-usSurveyMil :: Fractional a => Unit 'NonMetric DLength a-usSurveyMil = mkUnitQ (ucum "[mil_us]" "mil" "mil") 0.001 $ usSurveyInch---- | One yard is three feet.------ This yard is based on the 'usSurveyFoot'. For the international yard,--- see 'yard'. Note that this is not the yard in routine use in the United States.------ See <https://en.wikipedia.org/wiki/Yard here> for further information.------ >>> 1 *~ usSurveyYard--- 0.9144018288036576 m------ prop> 1 *~ usSurveyYard === 3 *~ usSurveyFoot------ >>> 1 *~ usSurveyYard :: Length Rational--- 3600 % 3937 m-usSurveyYard :: (Fractional a) => Unit 'NonMetric DLength a-usSurveyYard = mkUnitQ (ucum "[yd_us]" "yd" "yard") 3 $ usSurveyFoot---- | One US survey mile is 5 280 US survey feet.------ This mile is based on the 'usSurveyFoot'. For the mile based on the international foot,--- see 'mile'. Note that this is not the mile in routine use in the United States.------ See <https://en.wikipedia.org/wiki/Mile#US_survey_mile here> for further information.------ >>> 1 *~ usSurveyMile--- 1609.3472186944373 m------ prop> 1 *~ usSurveyMile === 5280 *~ usSurveyFoot------ >>> 1 *~ usSurveyMile :: Length Rational--- 6336000 % 3937 m-usSurveyMile :: (Fractional a) => Unit 'NonMetric DLength a-usSurveyMile = mkUnitQ (ucum "[mi_us]" "mi" "mile") 5280 $ usSurveyFoot---- | One acre is 43 560 square feet.------ This acre is based on the 'usSurveyFoot'. For the acre based on the international foot,--- see 'acre'. While both acres are in use, the difference between them is of little consequence--- for most applications in which either is used. This is the only acre defined by the UCUM.------ See <https://en.wikipedia.org/wiki/Acre#Differences_between_international_and_US_survey_acres here> for further information.------ >>> 1 *~ usSurveyAcre--- 4046.872609874252 m^2------ prop> 1 *~ usSurveyAcre === 43560 *~ usSurveyFoot ^ pos2------ >>> 1 *~ usSurveyAcre :: Area Rational--- 62726400000 % 15499969 m^2-usSurveyAcre :: (Fractional a) => Unit 'NonMetric DArea a-usSurveyAcre = mkUnitQ (ucum "[acr_us]" "ac" "acre") 43560 $ square usSurveyFoot---- | One avoirdupois pound is a mass, exactly defined in terms of the kilogram by the international--- yard and pound agreement of 1959.------ See <https://en.wikipedia.org/wiki/Avoirdupois#Internationalization here> for further information.------ >>> 1 *~ poundMass--- 0.45359237 kg------ >>> 1 *~ poundMass :: Mass Rational--- 45359237 % 100000000 kg-poundMass :: Fractional a => Unit 'NonMetric DMass a-poundMass = mkUnitQ (ucum "[lb_av]" "lb" "pound") 0.45359237 $ kilo gram---- | One avoirdupois ounce is one sixteenth of a 'poundMass'.------ See <https://en.wikipedia.org/wiki/Ounce#International_avoirdupois_ounce here> for further information.------ >>> 1 *~ ounce--- 2.8349523125e-2 kg------ prop> 16 *~ ounce === 1 *~ poundMass------ >>> 1 *~ ounce :: Mass Rational--- 45359237 % 1600000000 kg-ounce :: Fractional a => Unit 'NonMetric DMass a-ounce = mkUnitQ (ucum "[oz_av]" "oz" "ounce") (1 Prelude./ 16) $ poundMass---- | One short ton is two thousand 'poundMass'.------ See <https://en.wikipedia.org/wiki/Short_ton#United_States here> for further information.------ >>> 1 *~ shortTon--- 907.18474 kg------ >>> 1 *~ shortTon :: Mass Rational--- 45359237 % 50000 kg-shortTon :: Fractional a => Unit 'NonMetric DMass a-shortTon = mkUnitQ (ucum "[ston_av]" "ton" "short ton") 2000 $ poundMass---- | The pound-force is equal to the gravitational force exerted on a mass--- of one avoirdupois pound on the surface of Earth.------ This definition is based on standard gravity (the 'gee') and the--- international avoirdupois 'poundMass'.------ See <https://en.wikipedia.org/wiki/Pound_%28force%29 here> for further information.------ >>> 1 *~ poundForce--- 4.4482216152605 m kg s^-2------ prop> 1 *~ poundForce === 1 *~ poundMass * (1 *~ gee)------ >>> 1 *~ poundForce :: Force Rational--- 8896443230521 % 2000000000000 m kg s^-2-poundForce :: Fractional a => Unit 'NonMetric DForce a-poundForce = mkUnitQ (ucum "[lbf_av]" "lbf" "pound force") 1 $ poundMass * gee---- | One mechanical horsepower is by definition the power necessary--- to apply a force of 550 'poundForce' through a distance of one 'foot'--- per 'second'.------ See <https://en.wikipedia.org/wiki/Horsepower#Mechanical_horsepower here> for further information.------ >>> 1 *~ horsepower--- 745.6998715822702 m^2 kg s^-3------ prop> 1 *~ horsepower === 550 *~ poundForce * (1 *~ foot) / (1 *~ second)------ >>> 1 *~ horsepower :: Power Rational--- 37284993579113511 % 50000000000000 m^2 kg s^-3-horsepower :: Fractional a => Unit 'NonMetric DPower a-horsepower = mkUnitQ (ucum "[HP]" "hp" "horsepower") 550 $ foot * poundForce / second---- | The slug is a unit of mass associated with Imperial units and United States customary units.--- It is a mass that accelerates by 1 foot per second per second when a force of one pound is exerted on it.------ This definition is based on standard gravity (the 'gee'), the international 'foot', and the international avoirdupois 'poundMass'.------ See <https://en.wikipedia.org/wiki/Slug_%28mass%29 here> for further information.------ >>> 1 *~ slug--- 14.593902937206364 kg------ >>> 1 *~ slug :: Mass Rational--- 8896443230521 % 609600000000 kg-slug :: Fractional a => Unit 'NonMetric DMass a-slug = mkUnitQ (dimensionalAtom "slug" "slug" "slug") 1 $ poundForce * (second^pos2) / foot---- | One psi is a pressure of one 'poundForce' per 'square' 'inch' of area.------ See <https://en.wikipedia.org/wiki/Pounds_per_square_inch here> for further information.------ >>> 1 *~ psi--- 6894.757293168362 m^-1 kg s^-2------ >>> 1 *~ psi :: Pressure Rational--- 8896443230521 % 1290320000 m^-1 kg s^-2-psi :: Fractional a => Unit 'NonMetric DPressure a-psi = mkUnitQ (ucum "[psi]" "psi" "pound per square inch") 1 $ poundForce / inch ^ pos2---- | One nautical mile is a unit of length, set by international agreement as being exactly 1 852 meters.------ Historically, it was defined as the distance spanned by one minute of arc along a meridian of the Earth.------ See <https://en.wikipedia.org/wiki/Nautical_mile here> for further information.------ >>> 1 *~ nauticalMile--- 1852.0 m------ >>> 1 *~ nauticalMile :: Length Rational--- 1852 % 1 m-nauticalMile :: (Num a) => Unit 'NonMetric DLength a-nauticalMile = mkUnitZ (ucum "[nmi_i]" "NM" "nautical mile") 1852 $ meter---- | One knot is a velocity equal to one 'nauticalMile' per 'hour'.------ See <https://en.wikipedia.org/wiki/Knot_%28unit%29 here> for further information.------ >>> 1 *~ knot--- 0.5144444444444445 m s^-1------ >>> 1 *~ knot :: Velocity Rational--- 463 % 900 m s^-1-knot :: (Fractional a) => Unit 'NonMetric DVelocity a-knot = mkUnitQ (ucum "[kt_i]" "kt" "knot") 1 $ nauticalMile / hour---- | One revolution is an angle equal to 2 pi radians; a full circle.------ See <https://en.wikipedia.org/wiki/Turn_%28geometry%29 here> for further information.------ >>> 1 *~ revolution--- 6.283185307179586------ prop> 1 *~ revolution === _2 * pi * (1 *~ radian)------ prop> 1 *~ revolution === 360 *~ degree-revolution :: (Floating a) => Unit 'NonMetric DOne a-revolution = mkUnitR (dimensionalAtom "rev" "rev" "revolution") (2 Prelude.* Prelude.pi) $ radian--solid :: (Floating a) => Unit 'NonMetric DOne a-solid = mkUnitR (dimensionalAtom "solid" "solid" "solid") (4 Prelude.* Prelude.pi) $ steradian--teaspoon :: (Fractional a) => Unit 'NonMetric DVolume a-teaspoon = mkUnitQ (ucum "[tsp_m]" "tsp" "teaspoon") 5 $ milli liter---- | One btu is is the 'QuantityOfHeat' required to raise the temperature--- of 1 avoirdupois 'poundMass' of liquid water by 1 'degreeFahrenheit' at a constant pressure of one 'atmosphere'.------ Because this value must be determined experimentally and varies with temperature, several standardized--- values of the btu have arisen. This is the value based on the International Steam Table calorie,--- defined by the Fifth International Conference on the Properties of Steam.------ See <https://en.wikipedia.org/wiki/British_thermal_unit#Definitions here> for further information.------ >>> 1 *~ btu--- 1055.05585262 m^2 kg s^-2------ >>> 1 *~ btu :: Energy Rational--- 52752792631 % 50000000 m^2 kg s^-2-btu :: Fractional a => Unit 'NonMetric DEnergy a-btu = mkUnitQ (ucum "[Btu_IT]" "btu" "British thermal unit") 1055.05585262 $ joule---{- $year--The IAU recommends <#note2 [2]> that:-- Although there are several different kinds of year (as there are- several kinds of day), it is best to regard a year as a julian- year of 365.25 days (31.5576 Ms) unless otherwise specified.---}---- | One mean Julian year is a unit of measurement of time defined as exactly 365.25 days of 86 400 'second's each.------ See <https://en.wikipedia.org/wiki/Julian_year_%28astronomy%29 here> for further information.------ >>> 1 *~ year--- 3.15576e7 s------ >>> 1 *~ year :: Time Rational--- 31557600 % 1 s-year :: Num a => Unit 'NonMetric DTime a-year = mkUnitZ (ucum "a_j" "a" "mean Julian year") 31557600 $ second---- | One mean Julian century is one hundred mean Julian 'year's.------ >>> 1 *~ century--- 3.15576e9 s------ >>> 1 *~ century :: Time Rational--- 3155760000 % 1 s-century :: Num a => Unit 'NonMetric DTime a-century = mkUnitZ (dimensionalAtom "c_j" "cen" "mean Julian century") 100 $ year--{- $pressure-units-It seems that nearly every area of application has its own customary unit for measuring pressure.-We include some of the common ones here. 'psi' was defined earlier.--}---- | The bar is exactly 100 000 'Numeric.Units.Dimensional.SIUnits.pascal'.------ From Wikipedia:------ It is about equal to the atmospheric pressure on Earth at sea level.------ >>> 1 *~ bar--- 100000.0 m^-1 kg s^-2------ >>> 1 *~ bar :: Pressure Rational--- 100000 % 1 m^-1 kg s^-2-bar :: (Num a) => Unit 'Metric DPressure a-bar = mkUnitZ (ucumMetric "bar" "bar" "bar") 1e5 $ pascal---- | The "standard atmosphere".------ From Wikipedia <#note3 [3]>:------ The standard atmosphere (atm) is an established constant. It is--- approximately equal to typical air pressure at earth mean sea--- level.------ >>> 1 *~ atmosphere--- 101325.0 m^-1 kg s^-2------ >>> 1 *~ atmosphere :: Pressure Rational--- 101325 % 1 m^-1 kg s^-2-atmosphere :: (Num a) => Unit 'NonMetric DPressure a-atmosphere = mkUnitZ (ucum "atm" "atm" "standard atmosphere") 101325 $ pascal---- | The "technical atmosphere"------ From Wikipedia:------ A technical atmosphere (symbol: at) is a non-SI unit of pressure equal--- to one kilogram-force per square centimeter.------ >>> 1 *~ technicalAtmosphere--- 98066.5 m^-1 kg s^-2------ >>> 1 *~ technicalAtmosphere :: Pressure Rational--- 196133 % 2 m^-1 kg s^-2-technicalAtmosphere :: (Fractional a) => Unit 'NonMetric DPressure a-technicalAtmosphere = mkUnitQ (ucum "att" "at" "technical atmosphere") 1 $ kilo gram * gee * centi meter ^ neg2---- | The conventional value for the pressure exerted by a 1 mm high column of mercury.------ Per Wikipedia <#note4 [4]>, one mmHg (millimeter of mercury) is defined as:------ The pressure exerted at the base of a column of fluid exactly 1 mm high,--- when the density of the fluid is exactly 13.5951 g/cm^3, at a place--- where the acceleration of gravity is exactly 9.80665 m/s^2.------ The chosen fluid density approximately corresponds to that of mercury--- at 0 deg. Under most conditions, 1 mmHg is approximately equal to 1 'torr'.------ >>> 1 *~ mmHg--- 133.322 m^-1 kg s^-2------ >>> 1 *~ mmHg :: Pressure Rational--- 66661 % 500 m^-1 kg s^-2-mmHg :: (Fractional a) => Unit 'NonMetric DPressure a-mmHg = milli mHg--mHg :: (Fractional a) => Unit 'Metric DPressure a-mHg = mkUnitQ (ucumMetric "m[Hg]" "m Hg" "meter of mercury") 133.3220 $ kilo pascal---- | The conventional value for the pressure exerted by a 1 inch high column of mercury.------ Column inches of mercury are also used to measure pressure, especially in--- meteorological or aeronautical contexts in the United States.------ This is the value defined by UCUM. For the value defined by NIST, see 'inHg_NIST'.------ >>> 1 *~ inHg--- 3386.3788 m^-1 kg s^-2------ >>> 1 *~ inHg :: Pressure Rational--- 8465947 % 2500 m^-1 kg s^-2-inHg :: (Fractional a) => Unit 'NonMetric DPressure a-inHg = inHg_UCUM---- | The conventional value for the pressure exerted by a 1 inch high column of mercury.------ Column inches of mercury are also used to measure pressure, especially in--- meteorological or aeronautical contexts in the United States.------ This is the value defined by UCUM. For the value defined by NIST, see 'inHg_NIST'.------ >>> 1 *~ inHg_UCUM--- 3386.3788 m^-1 kg s^-2------ >>> 1 *~ inHg_UCUM :: Pressure Rational--- 8465947 % 2500 m^-1 kg s^-2-inHg_UCUM :: (Fractional a) => Unit 'NonMetric DPressure a-inHg_UCUM = mkUnitQ (ucum "[in_i'Hg]" "in Hg" "inch of mercury") 1 $ mHg * inch / meter---- | The conventional value for the pressure exerted by a 1 inch high column of mercury.------ Column inches of mercury are also used to measure pressure, especially in--- meteorological or aeronautical contexts in the United States.------ This is the value defined by NIST. For the value defined by UCUM, see 'inHg_UCUM'.------ >>> 1 *~ inHg_NIST--- 3386.389 m^-1 kg s^-2------ >>> 1 *~ inHg_NIST :: Pressure Rational--- 3386389 % 1000 m^-1 kg s^-2-inHg_NIST :: (Fractional a) => Unit 'NonMetric DPressure a-inHg_NIST = mkUnitQ (dimensionalAtom "[in_i'Hg_NIST]" "in Hg" "inch of mercury") 3.386389e3 $ pascal---- | One torr (symbol: Torr) is defined as 1/760 'atmosphere', which is approximately equal to 1 'mmHg'.------ See <https://en.wikipedia.org/wiki/Torr here> for further information.------ >>> 1 *~ torr--- 133.32236842105263 m^-1 kg s^-2------ >>> 1 *~ torr :: Pressure Rational--- 20265 % 152 m^-1 kg s^-2-torr :: (Fractional a) => Unit 'NonMetric DPressure a-torr = mkUnitQ (dimensionalAtom "Torr" "Torr" "Torr") (1 Prelude./ 760) $ atmosphere---- | The rad is a deprecated unit of 'AbsorbedDose', defined as--- 0.01 'gray'.------ See <https://en.wikipedia.org/wiki/Rad_%28unit%29 here> for further information.------ >>> 1 *~ rad--- 1.0e-2 m^2 s^-2------ >>> 1 *~ rad :: AbsorbedDose Rational--- 1 % 100 m^2 s^-2-rad :: (Fractional a) => Unit 'Metric DAbsorbedDose a-rad = mkUnitQ (ucumMetric "RAD" "RAD" "RAD") 1 $ centi gray---- | One Stokes is a unit of 'KinematicViscosity' equal to @1 cm^2 / s@.------ See <https://en.wikipedia.org/wiki/Viscosity#Kinematic_viscosity_.CE.BD here> for further information.------ >>> 1 *~ stokes--- 1.0e-4 m^2 s^-1------ >>> 1 *~ stokes :: KinematicViscosity Rational--- 1 % 10000 m^2 s^-1-stokes :: (Fractional a) => Unit 'Metric DKinematicViscosity a-stokes = mkUnitQ (ucumMetric "St" "St" "Stokes") 1 $ centi meter ^ pos2 / second--{- $temperature-These units of temperature are relative. For absolute temperatures, see 'Numeric.Units.Dimensional.SIUnits.fromDegreeCelsiusAbsolute'.--}---- | One degree Fahrenheit is a unit of relative temperature equal to 5/9 'kelvin'.------ Note that although the Fahrenheit scale is an absolute temperature scale, this unit is a unit of difference within--- that scale and measures relative temperature.------ See <https://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions here> for further information.------ >>> 1 *~ degreeFahrenheit--- 0.5555555555555556 K------ >>> 1 *~ degreeFahrenheit :: ThermodynamicTemperature Rational--- 5 % 9 K-degreeFahrenheit :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a-degreeFahrenheit = mkUnitQ (ucum "[degF]" "°F" "degree Fahrenheit") (5 Prelude./ 9) $ degreeCelsius---- | One degree Rankine is a unit of relative temperature equal to 5/9 'kelvin'.------ Note that although the Rankine scale is an absolute temperature scale, this unit is a unit of difference within--- that scale and measures relative temperature.------ See <https://en.wikipedia.org/wiki/Rankine_scale here> for further information.------ >>> 1 *~ degreeRankine--- 0.5555555555555556 K------ >>> 1 *~ degreeRankine :: ThermodynamicTemperature Rational--- 5 % 9 K-degreeRankine :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a-degreeRankine = mkUnitQ (ucum "[degR]" "°R" "degree Rankine") 1 $ degreeFahrenheit--{- $imperial-volumes-Per http://en.wikipedia.org/wiki/Imperial_units and http://en.wikipedia.org/wiki/Cup_(unit)#Imperial_cup.--}---- | One imperial gallon is defined exactly in terms of the 'liter'--- by the Weights and Measures Act 1985.------ See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information.------ >>> 1 *~ imperialGallon--- 4.54609e-3 m^3------ >>> 1 *~ imperialGallon :: Volume Rational--- 454609 % 100000000 m^3-imperialGallon :: (Fractional a) => Unit 'NonMetric DVolume a-imperialGallon = mkUnitQ (ucum "[gal_br]" "gal" "gallon") 4.54609 $ liter---- | One imperial quart is one quarter of an 'imperialGallon'.------ See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information.------ >>> 1 *~ imperialQuart--- 1.1365225e-3 m^3------ >>> 1 *~ imperialQuart :: Volume Rational--- 454609 % 400000000 m^3-imperialQuart :: (Fractional a) => Unit 'NonMetric DVolume a-imperialQuart = mkUnitQ (ucum "[qt_br]" "qt" "quart") (1 Prelude./ 4) $ imperialGallon---- | One imperial pint is one half of an 'imperialQuart'.------ See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information.------ >>> 1 *~ imperialPint--- 5.6826125e-4 m^3------ >>> 1 *~ imperialPint :: Volume Rational--- 454609 % 800000000 m^3-imperialPint :: (Fractional a) => Unit 'NonMetric DVolume a-imperialPint = mkUnitQ (ucum "[pt_br]" "pt" "pint") (1 Prelude./ 8) $ imperialGallon---- | One imperial cup is one half of an 'imperialPint'.------ This unit is not in common use and is does not appear in some sources--- describing the imperial fluid volume units.------ See <https://en.wikipedia.org/wiki/Cup_%28unit%29#Imperial_cup here> for further information.------ >>> 1 *~ imperialCup--- 2.84130625e-4 m^3------ >>> 1 *~ imperialCup :: Volume Rational--- 454609 % 1600000000 m^3-imperialCup :: (Fractional a) => Unit 'NonMetric DVolume a-imperialCup = mkUnitQ (dimensionalAtom "[cup_br]" "cup" "cup") 0.5 $ imperialPint---- | One imperial gill is one quarter of an 'imperialPint'.------ See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information.------ >>> 1 *~ imperialGill--- 1.420653125e-4 m^3------ >>> 1 *~ imperialGill :: Volume Rational--- 454609 % 3200000000 m^3-imperialGill :: (Fractional a) => Unit 'NonMetric DVolume a-imperialGill = mkUnitQ (ucum "[gil_br]" "gill" "gill") (1 Prelude./ 4) $ imperialPint---- | One imperial fluid ounce is one twentieth of an 'imperialPint'.------ See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information.------ >>> 1 *~ imperialFluidOunce--- 2.84130625e-5 m^3------ >>> 1 *~ imperialFluidOunce :: Volume Rational--- 454609 % 16000000000 m^3-imperialFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a-imperialFluidOunce = mkUnitQ (ucum "[foz_br]" "fl oz" "fluid ounce") (1 Prelude./ 20) $ imperialPint--{- $us-customary-volumes-Per http://www.nist.gov/pml/wmd/pubs/upload/2012-hb44-final.pdf page 452 and http://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume-Note that there exist rarely-used "dry" variants of units with overlapping names.--}---- | One US liquid gallon is a volume of 231 cubic inches.------ See <https://en.wikipedia.org/wiki/Gallon#The_US_liquid_gallon here> for further information.------ >>> 1 *~ usGallon--- 3.785411784e-3 m^3------ >>> 1 *~ usGallon :: Volume Rational--- 473176473 % 125000000000 m^3-usGallon :: (Fractional a) => Unit 'NonMetric DVolume a-usGallon = mkUnitQ (ucum "[gal_us]" "gal" "gallon") 231 $ (cubic inch)---- | One US liquid quart is one quarter of a 'usGallon'.------ See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information.------ >>> 1 *~ usQuart--- 9.46352946e-4 m^3------ >>> 1 *~ usQuart :: Volume Rational--- 473176473 % 500000000000 m^3-usQuart :: (Fractional a) => Unit 'NonMetric DVolume a-usQuart = mkUnitQ (ucum "[qt_us]" "qt" "quart") (1 Prelude./ 4) $ usGallon---- | One US liquid pint is one half of a 'usQuart'.------ See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information.------ >>> 1 *~ usPint--- 4.73176473e-4 m^3------ >>> 1 *~ usPint :: Volume Rational--- 473176473 % 1000000000000 m^3-usPint :: (Fractional a) => Unit 'NonMetric DVolume a-usPint = mkUnitQ (ucum "[pt_us]" "pt" "pint") (1 Prelude./ 8) $ usGallon---- | One US liquid cup is one half of a 'usPint'.------ See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information.------ >>> 1 *~ usCup--- 2.365882365e-4 m^3------ >>> 1 *~ usCup :: Volume Rational--- 473176473 % 2000000000000 m^3-usCup :: (Fractional a) => Unit 'NonMetric DVolume a-usCup = mkUnitQ (ucum "[cup_us]" "cup" "cup") (1 Prelude./ 2) $ usPint---- | One US liquid gill is one half of a 'usCup'.------ See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information.------ >>> 1 *~ usGill--- 1.1829411825e-4 m^3------ >>> 1 *~ usGill :: Volume Rational--- 473176473 % 4000000000000 m^3-usGill :: (Fractional a) => Unit 'NonMetric DVolume a-usGill = mkUnitQ (ucum "[gil_us]" "gill" "gill") (1 Prelude./ 4) $ usPint---- | One US fluid ounce is 1/128 'usGallon' or 1/8 'usCup'.------ See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information.------ >>> 1 *~ usFluidOunce--- 2.95735295625e-5 m^3------ >>> 1 *~ usFluidOunce :: Volume Rational--- 473176473 % 16000000000000 m^3-usFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a-usFluidOunce = mkUnitQ (ucum "[foz_us]" "fl oz" "fluid ounce") (1 Prelude./ 16) $ usPint -- sic, does not match factor used in imperial system---- | One Ångström is 1/10 'nano' 'meter'.------ See <https://en.wikipedia.org/wiki/%C3%85ngstr%C3%B6m here> for further information.------ >>> 1 *~ angstrom--- 1.0e-10 m------ >>> 1 *~ angstrom :: Length Rational--- 1 % 10000000000 m-angstrom :: (Fractional a) => Unit 'NonMetric DLength a-angstrom = mkUnitQ (ucum "Ao" "Å" "Ångström") 0.1 $ nano meter---- | One Gauss is 1/10000 'tesla'.------ See <https://en.wikipedia.org/wiki/Gauss_%28unit%29 here> for further information.------ >>> 1 *~ gauss--- 1.0e-4 kg s^-2 A^-1------ >>> 1 *~ gauss :: MagneticFluxDensity Rational--- 1 % 10000 kg s^-2 A^-1-gauss :: (Fractional a) => Unit 'NonMetric DMagneticFluxDensity a-gauss = mkUnitQ (ucum "G" "G" "Gauss") 1e-4 $ tesla+{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NumDecimals #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + += Summary + +This module defines units that are not part of the SI, with the +exception of those defined in the "Numeric.Units.Dimensional.SIUnits" module (units outside +of the SI accepted for use with the SI). + +Any chapters, sections or tables referenced are from <#note1 [1]> unless +otherwise specified. + +== Neper, bel, shannon and the like + +The units of section 5.1.2 are purposefully (but not permanently) +omitted. In fact the logarithmic units (see section 8.7) are +problematic and it is not clear how to implement them. Perhaps with +a conversion function similar to for degrees Celsius. + += References + +1. #note1# http://physics.nist.gov/Pubs/SP811/ +2. #note2# http://www.iau.org/science/publications/proceedings_rules/units/ +3. #note3# http://en.m.wikipedia.org/wiki/Pressure +4. #note4# http://en.m.wikipedia.org/wiki/Torr + +-} + +module Numeric.Units.Dimensional.NonSI +( + -- * Units Defined By Experiment + -- $values-obtained-experimentally + electronVolt, unifiedAtomicMassUnit, dalton, + -- * Standard Gravity + gee, + -- * Inch-pound Units + -- $inch-pound-units + poundMass, ounce, poundForce, horsepower, btu, shortTon, + nauticalMile, knot, + revolution, solid, + slug, psi, + teaspoon, + -- ** International Foot + foot, inch, mil, yard, mile, acre, + -- ** US Survey Foot + usSurveyFoot, usSurveyInch, usSurveyMil, usSurveyYard, usSurveyMile, usSurveyAcre, + -- * Years + -- $year + year, century, + -- * Pressure Units + -- $pressure-units + bar, atmosphere, technicalAtmosphere, mmHg, inHg, inHg_UCUM, inHg_NIST, torr, + -- * Radiation Units + rad, + -- * Kinematic Viscosity + stokes, + -- * Temperature + -- $temperature + degreeFahrenheit, degreeRankine, + -- * Imperial Volumes + -- $imperial-volumes + imperialGallon, imperialQuart, imperialPint, imperialCup, imperialGill, imperialFluidOunce, + -- * US Customary Volumes + -- $us-customary-volumes + usGallon, usQuart, usPint, usCup, usGill, usFluidOunce, + -- * Atomic-Scale Units + angstrom, + -- * Units from the Centimeter-Gram-Second Electrostatic System of Units + gauss +) +where + +import Numeric.Units.Dimensional.Prelude +import Numeric.Units.Dimensional.UnitNames.Internal (ucumMetric, ucum, dimensionalAtom) +import qualified Prelude + +-- $setup +-- >>> import Data.ExactPi +-- >>> import Data.Function (on) +-- >>> import Numeric.Units.Dimensional.Coercion +-- >>> default (Double) +-- >>> :{ +-- >>> let infix 4 === +-- >>> (===) = areExactlyEqual `on` unQuantity :: Quantity d ExactPi -> Quantity d ExactPi -> Bool +-- >>> :} + +{- $values-obtained-experimentally + +From Table 7, units accepted for use with the SI whose values in SI units are +obtained experimentally. + +When <#note1 [1]> was published the electron volt had a standard combined +uncertainity of 0.00000049e-19 J and the unified atomic mass unit +had a combined uncertainty of 0.0000010e-27 kg. + +-} + +electronVolt :: Floating a => Unit 'Metric DEnergy a +electronVolt = mkUnitR (ucumMetric "eV" "eV" "electron volt") 1.60217733e-19 $ joule + +unifiedAtomicMassUnit :: Floating a => Unit 'Metric DMass a +unifiedAtomicMassUnit = mkUnitR (ucumMetric "u" "u" "atomic mass unit") 1.6605402e-27 $ kilo gram + +dalton :: Floating a => Unit 'Metric DMass a +dalton = mkUnitR (ucumMetric "u" "Da" "Dalton") 1 $ unifiedAtomicMassUnit + +-- | One gee is the standard value of the acceleration due to gravity at the +-- Earth's surface, as standardized by CIPM. +-- +-- Note that local values of acceleration due to gravity will differ from the +-- standard gravity. +-- +-- See <https://en.wikipedia.org/wiki/Standard_gravity here> for further information. +-- +-- >>> 1 *~ gee +-- 9.80665 m s^-2 +-- +-- >>> 1 *~ gee :: Acceleration Rational +-- 196133 % 20000 m s^-2 +gee :: Fractional a => Unit 'Metric DAcceleration a +gee = mkUnitQ (ucumMetric "[g]" "g" "gee") 9.80665 $ meter / second ^ pos2 + +{- $inch-pound-units +Some US customary (that is, inch-pound) units. +-} + +-- | One international foot is one third of an international 'yard'. +-- +-- See <https://en.wikipedia.org/wiki/Foot_%28unit%29#International_foot here> for further information. +-- +-- >>> 1 *~ foot +-- 0.3048 m +-- +-- prop> 3 *~ foot === 1 *~ yard +-- +-- >>> 1 *~ foot :: Length Rational +-- 381 % 1250 m +foot :: Fractional a => Unit 'NonMetric DLength a +foot = mkUnitQ (ucum "[ft_i]" "ft" "foot") (1 Prelude./ 3) $ yard + +-- | One inch is one twelth of a 'foot'. +-- +-- This inch is based on the international 'foot'. +-- +-- See <https://en.wikipedia.org/wiki/Inch#Modern_standardisation here> for further information. +-- +-- >>> 1 *~ inch +-- 2.54e-2 m +-- +-- prop> 12 *~ inch === 1 *~ foot +-- +-- >>> 1 *~ inch :: Length Rational +-- 127 % 5000 m +inch :: Fractional a => Unit 'NonMetric DLength a +inch = mkUnitQ (ucum "[in_i]" "in" "inch") (1 Prelude./ 12) $ foot + +-- | One mil is one thousandth of an 'inch'. +-- +-- This mil is based on the international 'inch'. +-- +-- See <https://en.wikipedia.org/wiki/Thousandth_of_an_inch here> for further information. +-- +-- >>> 1 *~ mil +-- 2.54e-5 m +-- +-- prop> 1000 *~ mil === 1 *~ inch +-- +-- >>> 1 *~ mil :: Length Rational +-- 127 % 5000000 m +mil :: Fractional a => Unit 'NonMetric DLength a +mil = mkUnitQ (ucum "[mil_i]" "mil" "mil") 0.001 $ inch + +-- | One yard, as defined by international agreement in 1959, is precisely +-- 0.9144 'meter'. +-- +-- See <https://en.wikipedia.org/wiki/Yard here> for further information. +-- +-- >>> 1 *~ yard +-- 0.9144 m +-- +-- >>> 1 *~ yard :: Length Rational +-- 1143 % 1250 m +yard :: (Fractional a) => Unit 'NonMetric DLength a +yard = mkUnitQ (ucum "[yd_i]" "yd" "yard") 0.9144 $ meter + +-- | One mile is 5 280 feet. +-- +-- This mile is based on the international 'foot'. +-- +-- See <https://en.wikipedia.org/wiki/Mile#International_mile here> for further information. +-- +-- >>> 1 *~ mile +-- 1609.344 m +-- +-- prop> 1 *~ mile === 5280 *~ foot +-- +-- >>> 1 *~ mile :: Length Rational +-- 201168 % 125 m +mile :: (Fractional a) => Unit 'NonMetric DLength a +mile = mkUnitQ (ucum "[mi_i]" "mi" "mile") 5280 $ foot + +-- | One acre is 43 560 square feet. +-- +-- This acre is based on the international 'foot'. For the acre based on the US Survey Foot, +-- see 'usSurveyAcre'. While both acres are in use, the difference between them is of little consequence +-- for most applications in which either is used. +-- +-- See <https://en.wikipedia.org/wiki/Acre#Differences_between_international_and_US_survey_acres here> for further information. +-- +-- >>> 1 *~ acre +-- 4046.8564224 m^2 +-- +-- prop> 1 *~ acre === 43560 *~ foot ^ pos2 +-- +-- >>> 1 *~ acre :: Area Rational +-- 316160658 % 78125 m^2 +acre :: (Fractional a) => Unit 'NonMetric DArea a +acre = mkUnitQ (dimensionalAtom "[acr_i]" "ac" "acre") 43560 $ square foot + +-- | One US survey foot is 1200/3937 'meter'. +-- +-- For the international foot, see 'foot'. Note that this is not the foot in routine use +-- in the United States. +-- +-- See <https://en.wikipedia.org/wiki/Foot_%28unit%29#US_survey_foot here> for further information. +-- +-- >>> 1 *~ usSurveyFoot +-- 0.3048006096012192 m +-- +-- >>> 1 *~ usSurveyFoot :: Length Rational +-- 1200 % 3937 m +usSurveyFoot :: Fractional a => Unit 'NonMetric DLength a +usSurveyFoot = mkUnitQ (ucum "[ft_us]" "ft" "foot") (1200 Prelude./ 3937) $ meter + +-- | One inch is one twelth of a foot. +-- +-- This inch is based on the 'usSurveyFoot'. For the inch based on the international foot, +-- see 'inch'. Note that this is not the inch in routine use in the United States. +-- +-- See <https://en.wikipedia.org/wiki/Inch here> for further information. +-- +-- >>> 1 *~ usSurveyInch +-- 2.54000508001016e-2 m +-- +-- prop> 12 *~ usSurveyInch === 1 *~ usSurveyFoot +-- +-- >>> 1 *~ usSurveyInch :: Length Rational +-- 100 % 3937 m +usSurveyInch :: Fractional a => Unit 'NonMetric DLength a +usSurveyInch = mkUnitQ (ucum "[in_us]" "in" "inch") (1 Prelude./ 12) $ usSurveyFoot + +-- | One mil is one thousandth of an inch. +-- +-- This mil is based on the 'usSurveyInch'. For the mil based on the international inch, +-- see 'mil'. Note that this is not the mil in routine use in the United States. +-- +-- See <https://en.wikipedia.org/wiki/Thousandth_of_an_inch here> for further information. +-- +-- >>> 1 *~ usSurveyMil +-- 2.54000508001016e-5 m +-- +-- prop> 1000 *~ usSurveyMil === 1 *~ usSurveyInch +-- +-- >>> 1 *~ usSurveyMil :: Length Rational +-- 1 % 39370 m +usSurveyMil :: Fractional a => Unit 'NonMetric DLength a +usSurveyMil = mkUnitQ (ucum "[mil_us]" "mil" "mil") 0.001 $ usSurveyInch + +-- | One yard is three feet. +-- +-- This yard is based on the 'usSurveyFoot'. For the international yard, +-- see 'yard'. Note that this is not the yard in routine use in the United States. +-- +-- See <https://en.wikipedia.org/wiki/Yard here> for further information. +-- +-- >>> 1 *~ usSurveyYard +-- 0.9144018288036576 m +-- +-- prop> 1 *~ usSurveyYard === 3 *~ usSurveyFoot +-- +-- >>> 1 *~ usSurveyYard :: Length Rational +-- 3600 % 3937 m +usSurveyYard :: (Fractional a) => Unit 'NonMetric DLength a +usSurveyYard = mkUnitQ (ucum "[yd_us]" "yd" "yard") 3 $ usSurveyFoot + +-- | One US survey mile is 5 280 US survey feet. +-- +-- This mile is based on the 'usSurveyFoot'. For the mile based on the international foot, +-- see 'mile'. Note that this is not the mile in routine use in the United States. +-- +-- See <https://en.wikipedia.org/wiki/Mile#US_survey_mile here> for further information. +-- +-- >>> 1 *~ usSurveyMile +-- 1609.3472186944373 m +-- +-- prop> 1 *~ usSurveyMile === 5280 *~ usSurveyFoot +-- +-- >>> 1 *~ usSurveyMile :: Length Rational +-- 6336000 % 3937 m +usSurveyMile :: (Fractional a) => Unit 'NonMetric DLength a +usSurveyMile = mkUnitQ (ucum "[mi_us]" "mi" "mile") 5280 $ usSurveyFoot + +-- | One acre is 43 560 square feet. +-- +-- This acre is based on the 'usSurveyFoot'. For the acre based on the international foot, +-- see 'acre'. While both acres are in use, the difference between them is of little consequence +-- for most applications in which either is used. This is the only acre defined by the UCUM. +-- +-- See <https://en.wikipedia.org/wiki/Acre#Differences_between_international_and_US_survey_acres here> for further information. +-- +-- >>> 1 *~ usSurveyAcre +-- 4046.872609874252 m^2 +-- +-- prop> 1 *~ usSurveyAcre === 43560 *~ usSurveyFoot ^ pos2 +-- +-- >>> 1 *~ usSurveyAcre :: Area Rational +-- 62726400000 % 15499969 m^2 +usSurveyAcre :: (Fractional a) => Unit 'NonMetric DArea a +usSurveyAcre = mkUnitQ (ucum "[acr_us]" "ac" "acre") 43560 $ square usSurveyFoot + +-- | One avoirdupois pound is a mass, exactly defined in terms of the kilogram by the international +-- yard and pound agreement of 1959. +-- +-- See <https://en.wikipedia.org/wiki/Avoirdupois#Internationalization here> for further information. +-- +-- >>> 1 *~ poundMass +-- 0.45359237 kg +-- +-- >>> 1 *~ poundMass :: Mass Rational +-- 45359237 % 100000000 kg +poundMass :: Fractional a => Unit 'NonMetric DMass a +poundMass = mkUnitQ (ucum "[lb_av]" "lb" "pound") 0.45359237 $ kilo gram + +-- | One avoirdupois ounce is one sixteenth of a 'poundMass'. +-- +-- See <https://en.wikipedia.org/wiki/Ounce#International_avoirdupois_ounce here> for further information. +-- +-- >>> 1 *~ ounce +-- 2.8349523125e-2 kg +-- +-- prop> 16 *~ ounce === 1 *~ poundMass +-- +-- >>> 1 *~ ounce :: Mass Rational +-- 45359237 % 1600000000 kg +ounce :: Fractional a => Unit 'NonMetric DMass a +ounce = mkUnitQ (ucum "[oz_av]" "oz" "ounce") (1 Prelude./ 16) $ poundMass + +-- | One short ton is two thousand 'poundMass'. +-- +-- See <https://en.wikipedia.org/wiki/Short_ton#United_States here> for further information. +-- +-- >>> 1 *~ shortTon +-- 907.18474 kg +-- +-- >>> 1 *~ shortTon :: Mass Rational +-- 45359237 % 50000 kg +shortTon :: Fractional a => Unit 'NonMetric DMass a +shortTon = mkUnitQ (ucum "[ston_av]" "ton" "short ton") 2000 $ poundMass + +-- | The pound-force is equal to the gravitational force exerted on a mass +-- of one avoirdupois pound on the surface of Earth. +-- +-- This definition is based on standard gravity (the 'gee') and the +-- international avoirdupois 'poundMass'. +-- +-- See <https://en.wikipedia.org/wiki/Pound_%28force%29 here> for further information. +-- +-- >>> 1 *~ poundForce +-- 4.4482216152605 m kg s^-2 +-- +-- prop> 1 *~ poundForce === 1 *~ poundMass * (1 *~ gee) +-- +-- >>> 1 *~ poundForce :: Force Rational +-- 8896443230521 % 2000000000000 m kg s^-2 +poundForce :: Fractional a => Unit 'NonMetric DForce a +poundForce = mkUnitQ (ucum "[lbf_av]" "lbf" "pound force") 1 $ poundMass * gee + +-- | One mechanical horsepower is by definition the power necessary +-- to apply a force of 550 'poundForce' through a distance of one 'foot' +-- per 'second'. +-- +-- See <https://en.wikipedia.org/wiki/Horsepower#Mechanical_horsepower here> for further information. +-- +-- >>> 1 *~ horsepower +-- 745.6998715822702 m^2 kg s^-3 +-- +-- prop> 1 *~ horsepower === 550 *~ poundForce * (1 *~ foot) / (1 *~ second) +-- +-- >>> 1 *~ horsepower :: Power Rational +-- 37284993579113511 % 50000000000000 m^2 kg s^-3 +horsepower :: Fractional a => Unit 'NonMetric DPower a +horsepower = mkUnitQ (ucum "[HP]" "hp" "horsepower") 550 $ foot * poundForce / second + +-- | The slug is a unit of mass associated with Imperial units and United States customary units. +-- It is a mass that accelerates by 1 foot per second per second when a force of one pound is exerted on it. +-- +-- This definition is based on standard gravity (the 'gee'), the international 'foot', and the international avoirdupois 'poundMass'. +-- +-- See <https://en.wikipedia.org/wiki/Slug_%28mass%29 here> for further information. +-- +-- >>> 1 *~ slug +-- 14.593902937206364 kg +-- +-- >>> 1 *~ slug :: Mass Rational +-- 8896443230521 % 609600000000 kg +slug :: Fractional a => Unit 'NonMetric DMass a +slug = mkUnitQ (dimensionalAtom "slug" "slug" "slug") 1 $ poundForce * (second^pos2) / foot + +-- | One psi is a pressure of one 'poundForce' per 'square' 'inch' of area. +-- +-- See <https://en.wikipedia.org/wiki/Pounds_per_square_inch here> for further information. +-- +-- >>> 1 *~ psi +-- 6894.757293168362 m^-1 kg s^-2 +-- +-- >>> 1 *~ psi :: Pressure Rational +-- 8896443230521 % 1290320000 m^-1 kg s^-2 +psi :: Fractional a => Unit 'NonMetric DPressure a +psi = mkUnitQ (ucum "[psi]" "psi" "pound per square inch") 1 $ poundForce / inch ^ pos2 + +-- | One nautical mile is a unit of length, set by international agreement as being exactly 1 852 meters. +-- +-- Historically, it was defined as the distance spanned by one minute of arc along a meridian of the Earth. +-- +-- See <https://en.wikipedia.org/wiki/Nautical_mile here> for further information. +-- +-- >>> 1 *~ nauticalMile +-- 1852.0 m +-- +-- >>> 1 *~ nauticalMile :: Length Rational +-- 1852 % 1 m +nauticalMile :: (Num a) => Unit 'NonMetric DLength a +nauticalMile = mkUnitZ (ucum "[nmi_i]" "NM" "nautical mile") 1852 $ meter + +-- | One knot is a velocity equal to one 'nauticalMile' per 'hour'. +-- +-- See <https://en.wikipedia.org/wiki/Knot_%28unit%29 here> for further information. +-- +-- >>> 1 *~ knot +-- 0.5144444444444445 m s^-1 +-- +-- >>> 1 *~ knot :: Velocity Rational +-- 463 % 900 m s^-1 +knot :: (Fractional a) => Unit 'NonMetric DVelocity a +knot = mkUnitQ (ucum "[kt_i]" "kt" "knot") 1 $ nauticalMile / hour + +-- | One revolution is an angle equal to 2 pi radians; a full circle. +-- +-- See <https://en.wikipedia.org/wiki/Turn_%28geometry%29 here> for further information. +-- +-- >>> 1 *~ revolution +-- 6.283185307179586 +-- +-- prop> 1 *~ revolution === _2 * pi * (1 *~ radian) +-- +-- prop> 1 *~ revolution === 360 *~ degree +revolution :: (Floating a) => Unit 'NonMetric DOne a +revolution = mkUnitR (dimensionalAtom "rev" "rev" "revolution") (2 Prelude.* Prelude.pi) $ radian + +solid :: (Floating a) => Unit 'NonMetric DOne a +solid = mkUnitR (dimensionalAtom "solid" "solid" "solid") (4 Prelude.* Prelude.pi) $ steradian + +teaspoon :: (Fractional a) => Unit 'NonMetric DVolume a +teaspoon = mkUnitQ (ucum "[tsp_m]" "tsp" "teaspoon") 5 $ milli liter + +-- | One btu is is the 'QuantityOfHeat' required to raise the temperature +-- of 1 avoirdupois 'poundMass' of liquid water by 1 'degreeFahrenheit' at a constant pressure of one 'atmosphere'. +-- +-- Because this value must be determined experimentally and varies with temperature, several standardized +-- values of the btu have arisen. This is the value based on the International Steam Table calorie, +-- defined by the Fifth International Conference on the Properties of Steam. +-- +-- See <https://en.wikipedia.org/wiki/British_thermal_unit#Definitions here> for further information. +-- +-- >>> 1 *~ btu +-- 1055.05585262 m^2 kg s^-2 +-- +-- >>> 1 *~ btu :: Energy Rational +-- 52752792631 % 50000000 m^2 kg s^-2 +btu :: Fractional a => Unit 'NonMetric DEnergy a +btu = mkUnitQ (ucum "[Btu_IT]" "btu" "British thermal unit") 1055.05585262 $ joule + + +{- $year + +The IAU recommends <#note2 [2]> that: + + Although there are several different kinds of year (as there are + several kinds of day), it is best to regard a year as a julian + year of 365.25 days (31.5576 Ms) unless otherwise specified. + +-} + +-- | One mean Julian year is a unit of measurement of time defined as exactly 365.25 days of 86 400 'second's each. +-- +-- See <https://en.wikipedia.org/wiki/Julian_year_%28astronomy%29 here> for further information. +-- +-- >>> 1 *~ year +-- 3.15576e7 s +-- +-- >>> 1 *~ year :: Time Rational +-- 31557600 % 1 s +year :: Num a => Unit 'NonMetric DTime a +year = mkUnitZ (ucum "a_j" "a" "mean Julian year") 31557600 $ second + +-- | One mean Julian century is one hundred mean Julian 'year's. +-- +-- >>> 1 *~ century +-- 3.15576e9 s +-- +-- >>> 1 *~ century :: Time Rational +-- 3155760000 % 1 s +century :: Num a => Unit 'NonMetric DTime a +century = mkUnitZ (dimensionalAtom "c_j" "cen" "mean Julian century") 100 $ year + +{- $pressure-units +It seems that nearly every area of application has its own customary unit for measuring pressure. +We include some of the common ones here. 'psi' was defined earlier. +-} + +-- | The bar is exactly 100 000 'Numeric.Units.Dimensional.SIUnits.pascal'. +-- +-- From Wikipedia: +-- +-- It is about equal to the atmospheric pressure on Earth at sea level. +-- +-- >>> 1 *~ bar +-- 100000.0 m^-1 kg s^-2 +-- +-- >>> 1 *~ bar :: Pressure Rational +-- 100000 % 1 m^-1 kg s^-2 +bar :: (Num a) => Unit 'Metric DPressure a +bar = mkUnitZ (ucumMetric "bar" "bar" "bar") 1e5 $ pascal + +-- | The "standard atmosphere". +-- +-- From Wikipedia <#note3 [3]>: +-- +-- The standard atmosphere (atm) is an established constant. It is +-- approximately equal to typical air pressure at earth mean sea +-- level. +-- +-- >>> 1 *~ atmosphere +-- 101325.0 m^-1 kg s^-2 +-- +-- >>> 1 *~ atmosphere :: Pressure Rational +-- 101325 % 1 m^-1 kg s^-2 +atmosphere :: (Num a) => Unit 'NonMetric DPressure a +atmosphere = mkUnitZ (ucum "atm" "atm" "standard atmosphere") 101325 $ pascal + +-- | The "technical atmosphere" +-- +-- From Wikipedia: +-- +-- A technical atmosphere (symbol: at) is a non-SI unit of pressure equal +-- to one kilogram-force per square centimeter. +-- +-- >>> 1 *~ technicalAtmosphere +-- 98066.5 m^-1 kg s^-2 +-- +-- >>> 1 *~ technicalAtmosphere :: Pressure Rational +-- 196133 % 2 m^-1 kg s^-2 +technicalAtmosphere :: (Fractional a) => Unit 'NonMetric DPressure a +technicalAtmosphere = mkUnitQ (ucum "att" "at" "technical atmosphere") 1 $ kilo gram * gee * centi meter ^ neg2 + +-- | The conventional value for the pressure exerted by a 1 mm high column of mercury. +-- +-- Per Wikipedia <#note4 [4]>, one mmHg (millimeter of mercury) is defined as: +-- +-- The pressure exerted at the base of a column of fluid exactly 1 mm high, +-- when the density of the fluid is exactly 13.5951 g/cm^3, at a place +-- where the acceleration of gravity is exactly 9.80665 m/s^2. +-- +-- The chosen fluid density approximately corresponds to that of mercury +-- at 0 deg. Under most conditions, 1 mmHg is approximately equal to 1 'torr'. +-- +-- >>> 1 *~ mmHg +-- 133.322 m^-1 kg s^-2 +-- +-- >>> 1 *~ mmHg :: Pressure Rational +-- 66661 % 500 m^-1 kg s^-2 +mmHg :: (Fractional a) => Unit 'NonMetric DPressure a +mmHg = milli mHg + +mHg :: (Fractional a) => Unit 'Metric DPressure a +mHg = mkUnitQ (ucumMetric "m[Hg]" "m Hg" "meter of mercury") 133.3220 $ kilo pascal + +-- | The conventional value for the pressure exerted by a 1 inch high column of mercury. +-- +-- Column inches of mercury are also used to measure pressure, especially in +-- meteorological or aeronautical contexts in the United States. +-- +-- This is the value defined by UCUM. For the value defined by NIST, see 'inHg_NIST'. +-- +-- >>> 1 *~ inHg +-- 3386.3788 m^-1 kg s^-2 +-- +-- >>> 1 *~ inHg :: Pressure Rational +-- 8465947 % 2500 m^-1 kg s^-2 +inHg :: (Fractional a) => Unit 'NonMetric DPressure a +inHg = inHg_UCUM + +-- | The conventional value for the pressure exerted by a 1 inch high column of mercury. +-- +-- Column inches of mercury are also used to measure pressure, especially in +-- meteorological or aeronautical contexts in the United States. +-- +-- This is the value defined by UCUM. For the value defined by NIST, see 'inHg_NIST'. +-- +-- >>> 1 *~ inHg_UCUM +-- 3386.3788 m^-1 kg s^-2 +-- +-- >>> 1 *~ inHg_UCUM :: Pressure Rational +-- 8465947 % 2500 m^-1 kg s^-2 +inHg_UCUM :: (Fractional a) => Unit 'NonMetric DPressure a +inHg_UCUM = mkUnitQ (ucum "[in_i'Hg]" "in Hg" "inch of mercury") 1 $ mHg * inch / meter + +-- | The conventional value for the pressure exerted by a 1 inch high column of mercury. +-- +-- Column inches of mercury are also used to measure pressure, especially in +-- meteorological or aeronautical contexts in the United States. +-- +-- This is the value defined by NIST. For the value defined by UCUM, see 'inHg_UCUM'. +-- +-- >>> 1 *~ inHg_NIST +-- 3386.389 m^-1 kg s^-2 +-- +-- >>> 1 *~ inHg_NIST :: Pressure Rational +-- 3386389 % 1000 m^-1 kg s^-2 +inHg_NIST :: (Fractional a) => Unit 'NonMetric DPressure a +inHg_NIST = mkUnitQ (dimensionalAtom "[in_i'Hg_NIST]" "in Hg" "inch of mercury") 3.386389e3 $ pascal + +-- | One torr (symbol: Torr) is defined as 1/760 'atmosphere', which is approximately equal to 1 'mmHg'. +-- +-- See <https://en.wikipedia.org/wiki/Torr here> for further information. +-- +-- >>> 1 *~ torr +-- 133.32236842105263 m^-1 kg s^-2 +-- +-- >>> 1 *~ torr :: Pressure Rational +-- 20265 % 152 m^-1 kg s^-2 +torr :: (Fractional a) => Unit 'NonMetric DPressure a +torr = mkUnitQ (dimensionalAtom "Torr" "Torr" "Torr") (1 Prelude./ 760) $ atmosphere + +-- | The rad is a deprecated unit of 'AbsorbedDose', defined as +-- 0.01 'gray'. +-- +-- See <https://en.wikipedia.org/wiki/Rad_%28unit%29 here> for further information. +-- +-- >>> 1 *~ rad +-- 1.0e-2 m^2 s^-2 +-- +-- >>> 1 *~ rad :: AbsorbedDose Rational +-- 1 % 100 m^2 s^-2 +rad :: (Fractional a) => Unit 'Metric DAbsorbedDose a +rad = mkUnitQ (ucumMetric "RAD" "RAD" "RAD") 1 $ centi gray + +-- | One Stokes is a unit of 'KinematicViscosity' equal to @1 cm^2 / s@. +-- +-- See <https://en.wikipedia.org/wiki/Viscosity#Kinematic_viscosity_.CE.BD here> for further information. +-- +-- >>> 1 *~ stokes +-- 1.0e-4 m^2 s^-1 +-- +-- >>> 1 *~ stokes :: KinematicViscosity Rational +-- 1 % 10000 m^2 s^-1 +stokes :: (Fractional a) => Unit 'Metric DKinematicViscosity a +stokes = mkUnitQ (ucumMetric "St" "St" "Stokes") 1 $ centi meter ^ pos2 / second + +{- $temperature +These units of temperature are relative. For absolute temperatures, see 'Numeric.Units.Dimensional.SIUnits.fromDegreeCelsiusAbsolute'. +-} + +-- | One degree Fahrenheit is a unit of relative temperature equal to 5/9 'kelvin'. +-- +-- Note that although the Fahrenheit scale is an absolute temperature scale, this unit is a unit of difference within +-- that scale and measures relative temperature. +-- +-- See <https://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions here> for further information. +-- +-- >>> 1 *~ degreeFahrenheit +-- 0.5555555555555556 K +-- +-- >>> 1 *~ degreeFahrenheit :: ThermodynamicTemperature Rational +-- 5 % 9 K +degreeFahrenheit :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a +degreeFahrenheit = mkUnitQ (ucum "[degF]" "°F" "degree Fahrenheit") (5 Prelude./ 9) $ degreeCelsius + +-- | One degree Rankine is a unit of relative temperature equal to 5/9 'kelvin'. +-- +-- Note that although the Rankine scale is an absolute temperature scale, this unit is a unit of difference within +-- that scale and measures relative temperature. +-- +-- See <https://en.wikipedia.org/wiki/Rankine_scale here> for further information. +-- +-- >>> 1 *~ degreeRankine +-- 0.5555555555555556 K +-- +-- >>> 1 *~ degreeRankine :: ThermodynamicTemperature Rational +-- 5 % 9 K +degreeRankine :: (Fractional a) => Unit 'NonMetric DThermodynamicTemperature a +degreeRankine = mkUnitQ (ucum "[degR]" "°R" "degree Rankine") 1 $ degreeFahrenheit + +{- $imperial-volumes +Per http://en.wikipedia.org/wiki/Imperial_units and http://en.wikipedia.org/wiki/Cup_(unit)#Imperial_cup. +-} + +-- | One imperial gallon is defined exactly in terms of the 'liter' +-- by the Weights and Measures Act 1985. +-- +-- See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information. +-- +-- >>> 1 *~ imperialGallon +-- 4.54609e-3 m^3 +-- +-- >>> 1 *~ imperialGallon :: Volume Rational +-- 454609 % 100000000 m^3 +imperialGallon :: (Fractional a) => Unit 'NonMetric DVolume a +imperialGallon = mkUnitQ (ucum "[gal_br]" "gal" "gallon") 4.54609 $ liter + +-- | One imperial quart is one quarter of an 'imperialGallon'. +-- +-- See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information. +-- +-- >>> 1 *~ imperialQuart +-- 1.1365225e-3 m^3 +-- +-- >>> 1 *~ imperialQuart :: Volume Rational +-- 454609 % 400000000 m^3 +imperialQuart :: (Fractional a) => Unit 'NonMetric DVolume a +imperialQuart = mkUnitQ (ucum "[qt_br]" "qt" "quart") (1 Prelude./ 4) $ imperialGallon + +-- | One imperial pint is one half of an 'imperialQuart'. +-- +-- See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information. +-- +-- >>> 1 *~ imperialPint +-- 5.6826125e-4 m^3 +-- +-- >>> 1 *~ imperialPint :: Volume Rational +-- 454609 % 800000000 m^3 +imperialPint :: (Fractional a) => Unit 'NonMetric DVolume a +imperialPint = mkUnitQ (ucum "[pt_br]" "pt" "pint") (1 Prelude./ 8) $ imperialGallon + +-- | One imperial cup is one half of an 'imperialPint'. +-- +-- This unit is not in common use and is does not appear in some sources +-- describing the imperial fluid volume units. +-- +-- See <https://en.wikipedia.org/wiki/Cup_%28unit%29#Imperial_cup here> for further information. +-- +-- >>> 1 *~ imperialCup +-- 2.84130625e-4 m^3 +-- +-- >>> 1 *~ imperialCup :: Volume Rational +-- 454609 % 1600000000 m^3 +imperialCup :: (Fractional a) => Unit 'NonMetric DVolume a +imperialCup = mkUnitQ (dimensionalAtom "[cup_br]" "cup" "cup") 0.5 $ imperialPint + +-- | One imperial gill is one quarter of an 'imperialPint'. +-- +-- See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information. +-- +-- >>> 1 *~ imperialGill +-- 1.420653125e-4 m^3 +-- +-- >>> 1 *~ imperialGill :: Volume Rational +-- 454609 % 3200000000 m^3 +imperialGill :: (Fractional a) => Unit 'NonMetric DVolume a +imperialGill = mkUnitQ (ucum "[gil_br]" "gill" "gill") (1 Prelude./ 4) $ imperialPint + +-- | One imperial fluid ounce is one twentieth of an 'imperialPint'. +-- +-- See <https://en.wikipedia.org/wiki/Imperial_units#Volume here> for further information. +-- +-- >>> 1 *~ imperialFluidOunce +-- 2.84130625e-5 m^3 +-- +-- >>> 1 *~ imperialFluidOunce :: Volume Rational +-- 454609 % 16000000000 m^3 +imperialFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a +imperialFluidOunce = mkUnitQ (ucum "[foz_br]" "fl oz" "fluid ounce") (1 Prelude./ 20) $ imperialPint + +{- $us-customary-volumes +Per http://www.nist.gov/pml/wmd/pubs/upload/2012-hb44-final.pdf page 452 and http://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume +Note that there exist rarely-used "dry" variants of units with overlapping names. +-} + +-- | One US liquid gallon is a volume of 231 cubic inches. +-- +-- See <https://en.wikipedia.org/wiki/Gallon#The_US_liquid_gallon here> for further information. +-- +-- >>> 1 *~ usGallon +-- 3.785411784e-3 m^3 +-- +-- >>> 1 *~ usGallon :: Volume Rational +-- 473176473 % 125000000000 m^3 +usGallon :: (Fractional a) => Unit 'NonMetric DVolume a +usGallon = mkUnitQ (ucum "[gal_us]" "gal" "gallon") 231 $ (cubic inch) + +-- | One US liquid quart is one quarter of a 'usGallon'. +-- +-- See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information. +-- +-- >>> 1 *~ usQuart +-- 9.46352946e-4 m^3 +-- +-- >>> 1 *~ usQuart :: Volume Rational +-- 473176473 % 500000000000 m^3 +usQuart :: (Fractional a) => Unit 'NonMetric DVolume a +usQuart = mkUnitQ (ucum "[qt_us]" "qt" "quart") (1 Prelude./ 4) $ usGallon + +-- | One US liquid pint is one half of a 'usQuart'. +-- +-- See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information. +-- +-- >>> 1 *~ usPint +-- 4.73176473e-4 m^3 +-- +-- >>> 1 *~ usPint :: Volume Rational +-- 473176473 % 1000000000000 m^3 +usPint :: (Fractional a) => Unit 'NonMetric DVolume a +usPint = mkUnitQ (ucum "[pt_us]" "pt" "pint") (1 Prelude./ 8) $ usGallon + +-- | One US liquid cup is one half of a 'usPint'. +-- +-- See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information. +-- +-- >>> 1 *~ usCup +-- 2.365882365e-4 m^3 +-- +-- >>> 1 *~ usCup :: Volume Rational +-- 473176473 % 2000000000000 m^3 +usCup :: (Fractional a) => Unit 'NonMetric DVolume a +usCup = mkUnitQ (ucum "[cup_us]" "cup" "cup") (1 Prelude./ 2) $ usPint + +-- | One US liquid gill is one half of a 'usCup'. +-- +-- See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information. +-- +-- >>> 1 *~ usGill +-- 1.1829411825e-4 m^3 +-- +-- >>> 1 *~ usGill :: Volume Rational +-- 473176473 % 4000000000000 m^3 +usGill :: (Fractional a) => Unit 'NonMetric DVolume a +usGill = mkUnitQ (ucum "[gil_us]" "gill" "gill") (1 Prelude./ 4) $ usPint + +-- | One US fluid ounce is 1/128 'usGallon' or 1/8 'usCup'. +-- +-- See <https://en.wikipedia.org/wiki/United_States_customary_units#Fluid_volume here> for further information. +-- +-- >>> 1 *~ usFluidOunce +-- 2.95735295625e-5 m^3 +-- +-- >>> 1 *~ usFluidOunce :: Volume Rational +-- 473176473 % 16000000000000 m^3 +usFluidOunce :: (Fractional a) => Unit 'NonMetric DVolume a +usFluidOunce = mkUnitQ (ucum "[foz_us]" "fl oz" "fluid ounce") (1 Prelude./ 16) $ usPint -- sic, does not match factor used in imperial system + +-- | One Ångström is 1/10 'nano' 'meter'. +-- +-- See <https://en.wikipedia.org/wiki/%C3%85ngstr%C3%B6m here> for further information. +-- +-- >>> 1 *~ angstrom +-- 1.0e-10 m +-- +-- >>> 1 *~ angstrom :: Length Rational +-- 1 % 10000000000 m +angstrom :: (Fractional a) => Unit 'NonMetric DLength a +angstrom = mkUnitQ (ucum "Ao" "Å" "Ångström") 0.1 $ nano meter + +-- | One Gauss is 1/10000 'tesla'. +-- +-- See <https://en.wikipedia.org/wiki/Gauss_%28unit%29 here> for further information. +-- +-- >>> 1 *~ gauss +-- 1.0e-4 kg s^-2 A^-1 +-- +-- >>> 1 *~ gauss :: MagneticFluxDensity Rational +-- 1 % 10000 kg s^-2 A^-1 +gauss :: (Fractional a) => Unit 'NonMetric DMagneticFluxDensity a +gauss = mkUnitQ (ucum "G" "G" "Gauss") 1e-4 $ tesla
src/Numeric/Units/Dimensional/Prelude.hs view
@@ -1,52 +1,52 @@-{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--= Summary--This module supplies a convenient set of imports for working with the dimensional package, including aliases for common 'Quantity's and 'Dimension's,-and a comprehensive set of SI units and units accepted for use with the SI.--It re-exports the "Prelude", hiding arithmetic functions whose names collide with the dimensionally-typed versions supplied by this package.---}-module Numeric.Units.Dimensional.Prelude- ( module Numeric.Units.Dimensional- , module Numeric.Units.Dimensional.Quantities- , module Numeric.Units.Dimensional.SIUnits- , module Numeric.NumType.DK.Integers- , module Control.Category- , module Data.Foldable- , module Prelude- ) where--import Numeric.Units.Dimensional hiding- ( dmap- )--import Numeric.Units.Dimensional.Quantities--import Numeric.Units.Dimensional.SIUnits--import Numeric.NumType.DK.Integers- ( neg5, neg4, neg3, neg2, neg1, zero, pos1, pos2, pos3, pos4, pos5- ) -- Used in exponents.--import Control.Category- ( Category(..) )--import Data.Foldable- ( minimum, maximum )--import Prelude hiding- ( (+), (-), (*), (/), (^), (**)- , abs, signum, negate, recip, pi, exp, log, logBase, sqrt- , sin, cos, tan, asin, acos, atan, atan2- , sinh, cosh, tanh, asinh, acosh, atanh- , sum, product, minimum, maximum- , id, (.)- ) -- Hide definitions overridden by 'Numeric.Dimensional'.+{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + += Summary + +This module supplies a convenient set of imports for working with the dimensional package, including aliases for common 'Quantity's and 'Dimension's, +and a comprehensive set of SI units and units accepted for use with the SI. + +It re-exports the "Prelude", hiding arithmetic functions whose names collide with the dimensionally-typed versions supplied by this package. + +-} +module Numeric.Units.Dimensional.Prelude + ( module Numeric.Units.Dimensional + , module Numeric.Units.Dimensional.Quantities + , module Numeric.Units.Dimensional.SIUnits + , module Numeric.NumType.DK.Integers + , module Control.Category + , module Data.Foldable + , module Prelude + ) where + +import Numeric.Units.Dimensional hiding + ( dmap + ) + +import Numeric.Units.Dimensional.Quantities + +import Numeric.Units.Dimensional.SIUnits + +import Numeric.NumType.DK.Integers + ( neg5, neg4, neg3, neg2, neg1, zero, pos1, pos2, pos3, pos4, pos5 + ) -- Used in exponents. + +import Control.Category + ( Category(..) ) + +import Data.Foldable + ( minimum, maximum ) + +import Prelude hiding + ( (+), (-), (*), (/), (^), (**) + , abs, signum, negate, recip, pi, exp, log, logBase, sqrt + , sin, cos, tan, asin, acos, atan, atan2 + , sinh, cosh, tanh, asinh, acosh, atanh + , sum, product, minimum, maximum + , id, (.) + ) -- Hide definitions overridden by 'Numeric.Dimensional'.
src/Numeric/Units/Dimensional/Quantities.hs view
@@ -1,435 +1,435 @@-{-# OPTIONS_HADDOCK show-extensions #-}--{-# LANGUAGE DataKinds #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--= Summary--This module defines type synonyms for common dimensionalities and-the associated quantity types. Additional dimensionalities and-quantity types will be added on an as-needed basis.--The definitions in this module are grouped so that a type synonym-for the dimensionality is defined first in terms of base dimension-exponents. Then a type synonym for the corresponding quantity type-is defined. If there are several quantity types with the same-dimensionality type synonyms are provided for each quantity type.--= References--1. #note1# http://physics.nist.gov/Pubs/SP811/---}--module Numeric.Units.Dimensional.Quantities-(- -- * Quantities from the NIST Guide- -- $nist-guide- Area, Volume, Velocity, Acceleration, WaveNumber, MassDensity, Density, SpecificVolume, CurrentDensity,- MagneticFieldStrength, AmountOfSubstanceConcentration, Concentration, Luminance,- -- $table3- PlaneAngle, SolidAngle, Frequency, Force, Pressure, Stress, Energy, Work, QuantityOfHeat, Power, RadiantFlux,- ElectricCharge, QuantityOfElectricity, ElectricPotential, PotentialDifference, ElectromotiveForce,- Capacitance, ElectricResistance, ElectricConductance, MagneticFlux, MagneticFluxDensity,- Inductance, LuminousFlux, Illuminance, CelsiusTemperature,- Activity, AbsorbedDose, SpecificEnergy, Kerma, DoseEquivalent, AmbientDoseEquivalent, DirectionalDoseEquivalent, PersonalDoseEquivalent, EquivalentDose, CatalyticActivity,- -- $table4- AngularVelocity, AngularAcceleration, DynamicViscosity, MomentOfForce, SurfaceTension, HeatFluxDensity,- Irradiance, RadiantIntensity, Radiance, HeatCapacity, Entropy, SpecificHeatCapacity, SpecificEntropy,- ThermalConductivity, EnergyDensity, ElectricFieldStrength, ElectricChargeDensity, ElectricFluxDensity, Permittivity, Permeability,- MolarEnergy, MolarEntropy, MolarHeatCapacity, Exposure, AbsorbedDoseRate,- -- * Quantities not from the NIST Guide- -- $not-nist-guide- Impulse, Momentum, MassFlow, VolumeFlow, GravitationalParameter, KinematicViscosity, FirstMassMoment, MomentOfInertia, AngularMomentum,- ThermalResistivity, ThermalConductance, ThermalResistance, HeatTransferCoefficient, ThermalAdmittance, ThermalInsulance,- Jerk, Angle, Thrust, Torque, EnergyPerUnitMass,- -- * Powers of Unit Lengths- -- $powers-of-length-units- square, cubic,- -- * Dimension Aliases- -- $dimension-aliases- DArea, DVolume, DVelocity, DAcceleration, DWaveNumber, DMassDensity, DDensity, DSpecificVolume, DCurrentDensity,- DMagneticFieldStrength, DAmountOfSubstanceConcentration, DConcentration, DLuminance,- DPlaneAngle, DSolidAngle, DFrequency, DForce, DPressure, DStress, DEnergy, DWork, DQuantityOfHeat, DPower, DRadiantFlux,- DElectricCharge, DQuantityOfElectricity, DElectricPotential, DPotentialDifference, DElectromotiveForce,- DCapacitance, DElectricResistance, DElectricConductance, DMagneticFlux, DMagneticFluxDensity,- DInductance, DLuminousFlux, DIlluminance, DCelsiusTemperature,- DActivity, DAbsorbedDose, DSpecificEnergy, DKerma, DDoseEquivalent, DAmbientDoseEquivalent, DDirectionalDoseEquivalent, DPersonalDoseEquivalent, DEquivalentDose, DCatalyticActivity,- DAngularVelocity, DAngularAcceleration, DDynamicViscosity, DMomentOfForce, DSurfaceTension, DHeatFluxDensity,- DIrradiance, DRadiantIntensity, DRadiance, DHeatCapacity, DEntropy, DSpecificHeatCapacity, DSpecificEntropy,- DThermalConductivity, DEnergyDensity, DElectricFieldStrength, DElectricChargeDensity, DElectricFluxDensity, DPermittivity, DPermeability,- DMolarEnergy, DMolarEntropy, DMolarHeatCapacity, DExposure, DAbsorbedDoseRate,- DImpulse, DMomentum, DMassFlow, DVolumeFlow, DGravitationalParameter, DKinematicViscosity, DFirstMassMoment, DMomentOfInertia, DAngularMomentum,- DThermalResistivity, DThermalConductance, DThermalResistance, DHeatTransferCoefficient, DThermalAdmittance, DThermalInsulance,- DJerk, DAngle, DThrust, DTorque, DEnergyPerUnitMass-)-where--import Numeric.Units.Dimensional- ( Dimension (Dim), Quantity, Dimensionless- , DOne, DLuminousIntensity, DThermodynamicTemperature- , Unit, DLength, (^) -- Used only for 'square' and 'cubic'.- , Metricality(..)- )-import Numeric.NumType.DK.Integers- ( TypeInt (Neg3, Neg2, Neg1, Zero, Pos1, Pos2, Pos3, Pos4)- , pos2, pos3 -- Used only for 'square' and 'cubic'.- )-import Prelude (Fractional)-import Data.Typeable--{- $nist-guide-The following quantities are all from the NIST publication "Guide-for the Use of the International System of Units (SI)" <#note1 [1]>. Any-chapters, sections or tables referenced are from <#note1 [1]> unless otherwise-specified.--For lack of better organization we provide definitions grouped by-table in <#note1 [1]>.--== Table 2--"Examples of SI derived units expressed in terms of SI base units."---}--{- $dimension-aliases-For each 'Quantity' alias supplied above, we also supply a corresponding 'Dimension' alias.--These dimension aliases may be convenient for supplying type signatures for 'Unit's or for other type-level dimensional programming.--}--type DArea = 'Dim 'Pos2 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero-type Area = Quantity DArea--type DVolume = 'Dim 'Pos3 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero-type Volume = Quantity DVolume--type DVelocity = 'Dim 'Pos1 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero-type Velocity = Quantity DVelocity--type DAcceleration = 'Dim 'Pos1 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero-type Acceleration = Quantity DAcceleration--type DWaveNumber = 'Dim 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero-type WaveNumber = Quantity DWaveNumber--type DMassDensity = 'Dim 'Neg3 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero-type DDensity = DMassDensity-type MassDensity = Quantity DMassDensity-type Density = MassDensity -- Short name.--type DSpecificVolume = 'Dim 'Pos3 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero-type SpecificVolume = Quantity DSpecificVolume--type DCurrentDensity = 'Dim 'Neg2 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero-type CurrentDensity = Quantity DCurrentDensity--type DMagneticFieldStrength = 'Dim 'Neg1 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero-type MagneticFieldStrength = Quantity DMagneticFieldStrength--type DAmountOfSubstanceConcentration = 'Dim 'Neg3 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero-type DConcentration = DAmountOfSubstanceConcentration-type AmountOfSubstanceConcentration = Quantity DAmountOfSubstanceConcentration-type Concentration = AmountOfSubstanceConcentration -- Short name.--type DLuminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1-type Luminance = Quantity DLuminance---{- $table3-== Table 3--SI coherent derived units with special names and symbols.---}--type DPlaneAngle = DOne-type PlaneAngle = Dimensionless--type DSolidAngle = DOne-type SolidAngle = Dimensionless--type DFrequency = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero-type Frequency = Quantity DFrequency--type DForce = 'Dim 'Pos1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero-type Force = Quantity DForce--type DPressure = 'Dim 'Neg1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero-type DStress = DPressure-type Pressure = Quantity DPressure-type Stress = Quantity DStress--type DEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero-type DWork = DEnergy-type DQuantityOfHeat = DEnergy-type Energy = Quantity DEnergy-type Work = Quantity DWork-type QuantityOfHeat = Quantity DQuantityOfHeat--type DPower = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero-type DRadiantFlux = DPower-type Power = Quantity DPower-type RadiantFlux = Quantity DRadiantFlux--type DElectricCharge = 'Dim 'Zero 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero-type DQuantityOfElectricity = DElectricCharge-type ElectricCharge = Quantity DElectricCharge-type QuantityOfElectricity = Quantity DQuantityOfElectricity--type DElectricPotential = 'Dim 'Pos2 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero-type DPotentialDifference = DElectricPotential-type DElectromotiveForce = DElectricPotential-type ElectricPotential = Quantity DElectricPotential-type PotentialDifference = Quantity DPotentialDifference-type ElectromotiveForce = Quantity DElectromotiveForce--type DCapacitance = 'Dim 'Neg2 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero-type Capacitance = Quantity DCapacitance--type DElectricResistance = 'Dim 'Pos2 'Pos1 'Neg3 'Neg2 'Zero 'Zero 'Zero-type ElectricResistance = Quantity DElectricResistance--type DElectricConductance = 'Dim 'Neg2 'Neg1 'Pos3 'Pos2 'Zero 'Zero 'Zero-type ElectricConductance = Quantity DElectricConductance--type DMagneticFlux = 'Dim 'Pos2 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero-type MagneticFlux = Quantity DMagneticFlux--type DMagneticFluxDensity = 'Dim 'Zero 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero-type MagneticFluxDensity = Quantity DMagneticFluxDensity--type DInductance = 'Dim 'Pos2 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero-type Inductance = Quantity DInductance--type DLuminousFlux = DLuminousIntensity-type LuminousFlux = Quantity DLuminousFlux--type DIlluminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1-type Illuminance = Quantity DIlluminance--type DCelsiusTemperature = DThermodynamicTemperature-type CelsiusTemperature = Quantity DCelsiusTemperature--type DActivity = DFrequency -- Activity of a radionuclide.-type Activity = Quantity DActivity--type DAbsorbedDose = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero-type DSpecificEnergy = DAbsorbedDose-type DKerma = DAbsorbedDose-type AbsorbedDose = Quantity DAbsorbedDose-type SpecificEnergy = Quantity DSpecificEnergy -- Specific energy imparted.-type Kerma = Quantity DKerma--type DDoseEquivalent = DAbsorbedDose-type DAmbientDoseEquivalent = DDoseEquivalent-type DDirectionalDoseEquivalent = DDoseEquivalent-type DPersonalDoseEquivalent = DDoseEquivalent-type DEquivalentDose = DDoseEquivalent-type DoseEquivalent = Quantity DDoseEquivalent-type AmbientDoseEquivalent = DoseEquivalent-type DirectionalDoseEquivalent = DoseEquivalent-type PersonalDoseEquivalent = DoseEquivalent-type EquivalentDose = DoseEquivalent--type DCatalyticActivity = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Pos1 'Zero-type CatalyticActivity = Quantity DCatalyticActivity--{- $table4-== Table 4--"Examples of SI coherent derived units expressed with the aid of SI derived-units having special names and symbols."--We use the same grouping as for table 2.---}--type DAngularVelocity = DFrequency-type AngularVelocity = Quantity DAngularVelocity--type DAngularAcceleration = 'Dim 'Zero 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero-type AngularAcceleration = Quantity DAngularAcceleration--type DDynamicViscosity = 'Dim 'Neg1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero-type DynamicViscosity = Quantity DDynamicViscosity--type DMomentOfForce = DEnergy-type MomentOfForce = Quantity DMomentOfForce--type DSurfaceTension = 'Dim 'Zero 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero-type SurfaceTension = Quantity DSurfaceTension--type DHeatFluxDensity = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero-type DIrradiance = DHeatFluxDensity-type HeatFluxDensity = Quantity DHeatFluxDensity-type Irradiance = Quantity DIrradiance--type DRadiantIntensity = DPower-type RadiantIntensity = Quantity DRadiantIntensity--type DRadiance = DIrradiance-type Radiance = Quantity DRadiance--type DHeatCapacity = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Zero 'Zero-type DEntropy = DHeatCapacity-type HeatCapacity = Quantity DHeatCapacity-type Entropy = Quantity DEntropy--type DSpecificHeatCapacity = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Neg1 'Zero 'Zero-type DSpecificEntropy = DSpecificHeatCapacity-type SpecificHeatCapacity = Quantity DSpecificHeatCapacity-type SpecificEntropy = Quantity DSpecificEntropy--{---Specific energy was already defined in table 3.---}--type DThermalConductivity = 'Dim 'Pos1 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero-type ThermalConductivity = Quantity DThermalConductivity--type DEnergyDensity = DPressure-type EnergyDensity = Quantity DEnergyDensity--type DElectricFieldStrength = 'Dim 'Pos1 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero-type ElectricFieldStrength = Quantity DElectricFieldStrength--type DElectricChargeDensity = 'Dim 'Neg3 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero-type ElectricChargeDensity = Quantity DElectricChargeDensity--type DElectricFluxDensity = 'Dim 'Neg2 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero-type ElectricFluxDensity = Quantity DElectricFluxDensity--type DPermittivity = 'Dim 'Neg3 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero-type Permittivity = Quantity DPermittivity--type DPermeability = 'Dim 'Pos1 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero-type Permeability = Quantity DPermeability--type DMolarEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Neg1 'Zero-type MolarEnergy = Quantity DMolarEnergy--type DMolarEntropy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Neg1 'Zero-type DMolarHeatCapacity = DMolarEntropy-type MolarEntropy = Quantity DMolarEntropy-type MolarHeatCapacity = Quantity DMolarHeatCapacity--type DExposure = 'Dim 'Zero 'Neg1 'Pos1 'Pos1 'Zero 'Zero 'Zero-type Exposure = Quantity DExposure -- Exposure to x and gamma rays.--type DAbsorbedDoseRate = 'Dim 'Pos2 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero-type AbsorbedDoseRate = Quantity DAbsorbedDoseRate--{- $not-nist-guide-Here we define additional quantities on an as-needed basis. We also-provide some synonyms that we anticipate will be useful.--}--type DImpulse = 'Dim 'Pos1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero-type Impulse = Quantity DImpulse--type DMomentum = DImpulse-type Momentum = Quantity DMomentum--type DMassFlow = 'Dim 'Zero 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero-type MassFlow = Quantity DMassFlow--type DVolumeFlow = 'Dim 'Pos3 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero-type VolumeFlow = Quantity DVolumeFlow--type DGravitationalParameter = 'Dim 'Pos3 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero-type GravitationalParameter = Quantity DGravitationalParameter--type DKinematicViscosity = 'Dim 'Pos2 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero-type KinematicViscosity = Quantity DKinematicViscosity--type DFirstMassMoment = 'Dim 'Pos1 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero-type FirstMassMoment = Quantity DFirstMassMoment--type DMomentOfInertia = 'Dim 'Pos2 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero-type MomentOfInertia = Quantity DMomentOfInertia--type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero-type AngularMomentum = Quantity DAngularMomentum--{---The reciprocal of thermal conductivity.---}--type DThermalResistivity = 'Dim 'Neg1 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero-type ThermalResistivity = Quantity DThermalResistivity--{---Thermal conductance and resistance quantities after http://en.wikipedia.org/wiki/Thermal_conductivity#Definitions.---}--type DThermalConductance = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero-type ThermalConductance = Quantity DThermalConductance--type DThermalResistance = 'Dim 'Neg2 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero-type ThermalResistance = Quantity DThermalResistance--type DHeatTransferCoefficient = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero-type HeatTransferCoefficient = Quantity DHeatTransferCoefficient--type DThermalAdmittance = DHeatTransferCoefficient-type ThermalAdmittance = HeatTransferCoefficient--type DThermalInsulance = 'Dim 'Zero 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero-type ThermalInsulance = Quantity DThermalInsulance--type DJerk = 'Dim 'Pos1 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero-type Jerk = Quantity DJerk--type Angle = PlaneAngle -- Abbreviation-type DAngle = DPlaneAngle -- Abbreviation--type Thrust = Force-type DThrust = DForce--type Torque = MomentOfForce-type DTorque = DMomentOfForce--type EnergyPerUnitMass = SpecificEnergy-type DEnergyPerUnitMass = DSpecificEnergy--{- $powers-of-length-units-It is permissible to express powers of length units by prefixing-'square' and 'cubic' (see section 9.6 "Spelling unit names raised-to powers" of <#note1 [1]>).--These definitions may seem slightly out of place but these is no-obvious place where they should be. Here they are at least close-to the definitions of 'DArea' and 'DVolume'.--}---- $setup--- >>> import Numeric.Units.Dimensional.Prelude---- | Constructs a unit of area from a unit of length, taking the area of a square whose sides are that length.------ >>> 64 *~ square meter == (8 *~ meter) ^ pos2--- True-square :: (Fractional a, Typeable m) => Unit m DLength a -> Unit 'NonMetric DArea a-square x = x ^ pos2---- | Constructs a unit of volume from a unit of length, taking the volume of a cube whose sides are that length.------ >>> 64 *~ cubic meter == (4 *~ meter) ^ pos3--- True-cubic :: (Fractional a, Typeable m) => Unit m DLength a -> Unit 'NonMetric DVolume a-cubic x = x ^ pos3+{-# OPTIONS_HADDOCK show-extensions #-} + +{-# LANGUAGE DataKinds #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + += Summary + +This module defines type synonyms for common dimensionalities and +the associated quantity types. Additional dimensionalities and +quantity types will be added on an as-needed basis. + +The definitions in this module are grouped so that a type synonym +for the dimensionality is defined first in terms of base dimension +exponents. Then a type synonym for the corresponding quantity type +is defined. If there are several quantity types with the same +dimensionality type synonyms are provided for each quantity type. + += References + +1. #note1# http://physics.nist.gov/Pubs/SP811/ + +-} + +module Numeric.Units.Dimensional.Quantities +( + -- * Quantities from the NIST Guide + -- $nist-guide + Area, Volume, Velocity, Acceleration, WaveNumber, MassDensity, Density, SpecificVolume, CurrentDensity, + MagneticFieldStrength, AmountOfSubstanceConcentration, Concentration, Luminance, + -- $table3 + PlaneAngle, SolidAngle, Frequency, Force, Pressure, Stress, Energy, Work, QuantityOfHeat, Power, RadiantFlux, + ElectricCharge, QuantityOfElectricity, ElectricPotential, PotentialDifference, ElectromotiveForce, + Capacitance, ElectricResistance, ElectricConductance, MagneticFlux, MagneticFluxDensity, + Inductance, LuminousFlux, Illuminance, CelsiusTemperature, + Activity, AbsorbedDose, SpecificEnergy, Kerma, DoseEquivalent, AmbientDoseEquivalent, DirectionalDoseEquivalent, PersonalDoseEquivalent, EquivalentDose, CatalyticActivity, + -- $table4 + AngularVelocity, AngularAcceleration, DynamicViscosity, MomentOfForce, SurfaceTension, HeatFluxDensity, + Irradiance, RadiantIntensity, Radiance, HeatCapacity, Entropy, SpecificHeatCapacity, SpecificEntropy, + ThermalConductivity, EnergyDensity, ElectricFieldStrength, ElectricChargeDensity, ElectricFluxDensity, Permittivity, Permeability, + MolarEnergy, MolarEntropy, MolarHeatCapacity, Exposure, AbsorbedDoseRate, + -- * Quantities not from the NIST Guide + -- $not-nist-guide + Impulse, Momentum, MassFlow, VolumeFlow, GravitationalParameter, KinematicViscosity, FirstMassMoment, MomentOfInertia, AngularMomentum, + ThermalResistivity, ThermalConductance, ThermalResistance, HeatTransferCoefficient, ThermalAdmittance, ThermalInsulance, + Jerk, Angle, Thrust, Torque, EnergyPerUnitMass, + -- * Powers of Unit Lengths + -- $powers-of-length-units + square, cubic, + -- * Dimension Aliases + -- $dimension-aliases + DArea, DVolume, DVelocity, DAcceleration, DWaveNumber, DMassDensity, DDensity, DSpecificVolume, DCurrentDensity, + DMagneticFieldStrength, DAmountOfSubstanceConcentration, DConcentration, DLuminance, + DPlaneAngle, DSolidAngle, DFrequency, DForce, DPressure, DStress, DEnergy, DWork, DQuantityOfHeat, DPower, DRadiantFlux, + DElectricCharge, DQuantityOfElectricity, DElectricPotential, DPotentialDifference, DElectromotiveForce, + DCapacitance, DElectricResistance, DElectricConductance, DMagneticFlux, DMagneticFluxDensity, + DInductance, DLuminousFlux, DIlluminance, DCelsiusTemperature, + DActivity, DAbsorbedDose, DSpecificEnergy, DKerma, DDoseEquivalent, DAmbientDoseEquivalent, DDirectionalDoseEquivalent, DPersonalDoseEquivalent, DEquivalentDose, DCatalyticActivity, + DAngularVelocity, DAngularAcceleration, DDynamicViscosity, DMomentOfForce, DSurfaceTension, DHeatFluxDensity, + DIrradiance, DRadiantIntensity, DRadiance, DHeatCapacity, DEntropy, DSpecificHeatCapacity, DSpecificEntropy, + DThermalConductivity, DEnergyDensity, DElectricFieldStrength, DElectricChargeDensity, DElectricFluxDensity, DPermittivity, DPermeability, + DMolarEnergy, DMolarEntropy, DMolarHeatCapacity, DExposure, DAbsorbedDoseRate, + DImpulse, DMomentum, DMassFlow, DVolumeFlow, DGravitationalParameter, DKinematicViscosity, DFirstMassMoment, DMomentOfInertia, DAngularMomentum, + DThermalResistivity, DThermalConductance, DThermalResistance, DHeatTransferCoefficient, DThermalAdmittance, DThermalInsulance, + DJerk, DAngle, DThrust, DTorque, DEnergyPerUnitMass +) +where + +import Numeric.Units.Dimensional + ( Dimension (Dim), Quantity, Dimensionless + , DOne, DLuminousIntensity, DThermodynamicTemperature + , Unit, DLength, (^) -- Used only for 'square' and 'cubic'. + , Metricality(..) + ) +import Numeric.NumType.DK.Integers + ( TypeInt (Neg3, Neg2, Neg1, Zero, Pos1, Pos2, Pos3, Pos4) + , pos2, pos3 -- Used only for 'square' and 'cubic'. + ) +import Prelude (Fractional) +import Data.Typeable + +{- $nist-guide +The following quantities are all from the NIST publication "Guide +for the Use of the International System of Units (SI)" <#note1 [1]>. Any +chapters, sections or tables referenced are from <#note1 [1]> unless otherwise +specified. + +For lack of better organization we provide definitions grouped by +table in <#note1 [1]>. + +== Table 2 + +"Examples of SI derived units expressed in terms of SI base units." + +-} + +{- $dimension-aliases +For each 'Quantity' alias supplied above, we also supply a corresponding 'Dimension' alias. + +These dimension aliases may be convenient for supplying type signatures for 'Unit's or for other type-level dimensional programming. +-} + +type DArea = 'Dim 'Pos2 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero +type Area = Quantity DArea + +type DVolume = 'Dim 'Pos3 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero +type Volume = Quantity DVolume + +type DVelocity = 'Dim 'Pos1 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero +type Velocity = Quantity DVelocity + +type DAcceleration = 'Dim 'Pos1 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero +type Acceleration = Quantity DAcceleration + +type DWaveNumber = 'Dim 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero +type WaveNumber = Quantity DWaveNumber + +type DMassDensity = 'Dim 'Neg3 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero +type DDensity = DMassDensity +type MassDensity = Quantity DMassDensity +type Density = MassDensity -- Short name. + +type DSpecificVolume = 'Dim 'Pos3 'Neg1 'Zero 'Zero 'Zero 'Zero 'Zero +type SpecificVolume = Quantity DSpecificVolume + +type DCurrentDensity = 'Dim 'Neg2 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero +type CurrentDensity = Quantity DCurrentDensity + +type DMagneticFieldStrength = 'Dim 'Neg1 'Zero 'Zero 'Pos1 'Zero 'Zero 'Zero +type MagneticFieldStrength = Quantity DMagneticFieldStrength + +type DAmountOfSubstanceConcentration = 'Dim 'Neg3 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero +type DConcentration = DAmountOfSubstanceConcentration +type AmountOfSubstanceConcentration = Quantity DAmountOfSubstanceConcentration +type Concentration = AmountOfSubstanceConcentration -- Short name. + +type DLuminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 +type Luminance = Quantity DLuminance + + +{- $table3 +== Table 3 + +SI coherent derived units with special names and symbols. + +-} + +type DPlaneAngle = DOne +type PlaneAngle = Dimensionless + +type DSolidAngle = DOne +type SolidAngle = Dimensionless + +type DFrequency = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero +type Frequency = Quantity DFrequency + +type DForce = 'Dim 'Pos1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero +type Force = Quantity DForce + +type DPressure = 'Dim 'Neg1 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero +type DStress = DPressure +type Pressure = Quantity DPressure +type Stress = Quantity DStress + +type DEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero +type DWork = DEnergy +type DQuantityOfHeat = DEnergy +type Energy = Quantity DEnergy +type Work = Quantity DWork +type QuantityOfHeat = Quantity DQuantityOfHeat + +type DPower = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero +type DRadiantFlux = DPower +type Power = Quantity DPower +type RadiantFlux = Quantity DRadiantFlux + +type DElectricCharge = 'Dim 'Zero 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero +type DQuantityOfElectricity = DElectricCharge +type ElectricCharge = Quantity DElectricCharge +type QuantityOfElectricity = Quantity DQuantityOfElectricity + +type DElectricPotential = 'Dim 'Pos2 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero +type DPotentialDifference = DElectricPotential +type DElectromotiveForce = DElectricPotential +type ElectricPotential = Quantity DElectricPotential +type PotentialDifference = Quantity DPotentialDifference +type ElectromotiveForce = Quantity DElectromotiveForce + +type DCapacitance = 'Dim 'Neg2 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero +type Capacitance = Quantity DCapacitance + +type DElectricResistance = 'Dim 'Pos2 'Pos1 'Neg3 'Neg2 'Zero 'Zero 'Zero +type ElectricResistance = Quantity DElectricResistance + +type DElectricConductance = 'Dim 'Neg2 'Neg1 'Pos3 'Pos2 'Zero 'Zero 'Zero +type ElectricConductance = Quantity DElectricConductance + +type DMagneticFlux = 'Dim 'Pos2 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero +type MagneticFlux = Quantity DMagneticFlux + +type DMagneticFluxDensity = 'Dim 'Zero 'Pos1 'Neg2 'Neg1 'Zero 'Zero 'Zero +type MagneticFluxDensity = Quantity DMagneticFluxDensity + +type DInductance = 'Dim 'Pos2 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero +type Inductance = Quantity DInductance + +type DLuminousFlux = DLuminousIntensity +type LuminousFlux = Quantity DLuminousFlux + +type DIlluminance = 'Dim 'Neg2 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 +type Illuminance = Quantity DIlluminance + +type DCelsiusTemperature = DThermodynamicTemperature +type CelsiusTemperature = Quantity DCelsiusTemperature + +type DActivity = DFrequency -- Activity of a radionuclide. +type Activity = Quantity DActivity + +type DAbsorbedDose = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero +type DSpecificEnergy = DAbsorbedDose +type DKerma = DAbsorbedDose +type AbsorbedDose = Quantity DAbsorbedDose +type SpecificEnergy = Quantity DSpecificEnergy -- Specific energy imparted. +type Kerma = Quantity DKerma + +type DDoseEquivalent = DAbsorbedDose +type DAmbientDoseEquivalent = DDoseEquivalent +type DDirectionalDoseEquivalent = DDoseEquivalent +type DPersonalDoseEquivalent = DDoseEquivalent +type DEquivalentDose = DDoseEquivalent +type DoseEquivalent = Quantity DDoseEquivalent +type AmbientDoseEquivalent = DoseEquivalent +type DirectionalDoseEquivalent = DoseEquivalent +type PersonalDoseEquivalent = DoseEquivalent +type EquivalentDose = DoseEquivalent + +type DCatalyticActivity = 'Dim 'Zero 'Zero 'Neg1 'Zero 'Zero 'Pos1 'Zero +type CatalyticActivity = Quantity DCatalyticActivity + +{- $table4 +== Table 4 + +"Examples of SI coherent derived units expressed with the aid of SI derived +units having special names and symbols." + +We use the same grouping as for table 2. + +-} + +type DAngularVelocity = DFrequency +type AngularVelocity = Quantity DAngularVelocity + +type DAngularAcceleration = 'Dim 'Zero 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero +type AngularAcceleration = Quantity DAngularAcceleration + +type DDynamicViscosity = 'Dim 'Neg1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero +type DynamicViscosity = Quantity DDynamicViscosity + +type DMomentOfForce = DEnergy +type MomentOfForce = Quantity DMomentOfForce + +type DSurfaceTension = 'Dim 'Zero 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero +type SurfaceTension = Quantity DSurfaceTension + +type DHeatFluxDensity = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Zero 'Zero 'Zero +type DIrradiance = DHeatFluxDensity +type HeatFluxDensity = Quantity DHeatFluxDensity +type Irradiance = Quantity DIrradiance + +type DRadiantIntensity = DPower +type RadiantIntensity = Quantity DRadiantIntensity + +type DRadiance = DIrradiance +type Radiance = Quantity DRadiance + +type DHeatCapacity = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Zero 'Zero +type DEntropy = DHeatCapacity +type HeatCapacity = Quantity DHeatCapacity +type Entropy = Quantity DEntropy + +type DSpecificHeatCapacity = 'Dim 'Pos2 'Zero 'Neg2 'Zero 'Neg1 'Zero 'Zero +type DSpecificEntropy = DSpecificHeatCapacity +type SpecificHeatCapacity = Quantity DSpecificHeatCapacity +type SpecificEntropy = Quantity DSpecificEntropy + +{- + +Specific energy was already defined in table 3. + +-} + +type DThermalConductivity = 'Dim 'Pos1 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero +type ThermalConductivity = Quantity DThermalConductivity + +type DEnergyDensity = DPressure +type EnergyDensity = Quantity DEnergyDensity + +type DElectricFieldStrength = 'Dim 'Pos1 'Pos1 'Neg3 'Neg1 'Zero 'Zero 'Zero +type ElectricFieldStrength = Quantity DElectricFieldStrength + +type DElectricChargeDensity = 'Dim 'Neg3 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero +type ElectricChargeDensity = Quantity DElectricChargeDensity + +type DElectricFluxDensity = 'Dim 'Neg2 'Zero 'Pos1 'Pos1 'Zero 'Zero 'Zero +type ElectricFluxDensity = Quantity DElectricFluxDensity + +type DPermittivity = 'Dim 'Neg3 'Neg1 'Pos4 'Pos2 'Zero 'Zero 'Zero +type Permittivity = Quantity DPermittivity + +type DPermeability = 'Dim 'Pos1 'Pos1 'Neg2 'Neg2 'Zero 'Zero 'Zero +type Permeability = Quantity DPermeability + +type DMolarEnergy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Neg1 'Zero +type MolarEnergy = Quantity DMolarEnergy + +type DMolarEntropy = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Neg1 'Neg1 'Zero +type DMolarHeatCapacity = DMolarEntropy +type MolarEntropy = Quantity DMolarEntropy +type MolarHeatCapacity = Quantity DMolarHeatCapacity + +type DExposure = 'Dim 'Zero 'Neg1 'Pos1 'Pos1 'Zero 'Zero 'Zero +type Exposure = Quantity DExposure -- Exposure to x and gamma rays. + +type DAbsorbedDoseRate = 'Dim 'Pos2 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero +type AbsorbedDoseRate = Quantity DAbsorbedDoseRate + +{- $not-nist-guide +Here we define additional quantities on an as-needed basis. We also +provide some synonyms that we anticipate will be useful. +-} + +type DImpulse = 'Dim 'Pos1 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero +type Impulse = Quantity DImpulse + +type DMomentum = DImpulse +type Momentum = Quantity DMomentum + +type DMassFlow = 'Dim 'Zero 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero +type MassFlow = Quantity DMassFlow + +type DVolumeFlow = 'Dim 'Pos3 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero +type VolumeFlow = Quantity DVolumeFlow + +type DGravitationalParameter = 'Dim 'Pos3 'Zero 'Neg2 'Zero 'Zero 'Zero 'Zero +type GravitationalParameter = Quantity DGravitationalParameter + +type DKinematicViscosity = 'Dim 'Pos2 'Zero 'Neg1 'Zero 'Zero 'Zero 'Zero +type KinematicViscosity = Quantity DKinematicViscosity + +type DFirstMassMoment = 'Dim 'Pos1 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero +type FirstMassMoment = Quantity DFirstMassMoment + +type DMomentOfInertia = 'Dim 'Pos2 'Pos1 'Zero 'Zero 'Zero 'Zero 'Zero +type MomentOfInertia = Quantity DMomentOfInertia + +type DAngularMomentum = 'Dim 'Pos2 'Pos1 'Neg1 'Zero 'Zero 'Zero 'Zero +type AngularMomentum = Quantity DAngularMomentum + +{- + +The reciprocal of thermal conductivity. + +-} + +type DThermalResistivity = 'Dim 'Neg1 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero +type ThermalResistivity = Quantity DThermalResistivity + +{- + +Thermal conductance and resistance quantities after http://en.wikipedia.org/wiki/Thermal_conductivity#Definitions. + +-} + +type DThermalConductance = 'Dim 'Pos2 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero +type ThermalConductance = Quantity DThermalConductance + +type DThermalResistance = 'Dim 'Neg2 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero +type ThermalResistance = Quantity DThermalResistance + +type DHeatTransferCoefficient = 'Dim 'Zero 'Pos1 'Neg3 'Zero 'Neg1 'Zero 'Zero +type HeatTransferCoefficient = Quantity DHeatTransferCoefficient + +type DThermalAdmittance = DHeatTransferCoefficient +type ThermalAdmittance = HeatTransferCoefficient + +type DThermalInsulance = 'Dim 'Zero 'Neg1 'Pos3 'Zero 'Pos1 'Zero 'Zero +type ThermalInsulance = Quantity DThermalInsulance + +type DJerk = 'Dim 'Pos1 'Zero 'Neg3 'Zero 'Zero 'Zero 'Zero +type Jerk = Quantity DJerk + +type Angle = PlaneAngle -- Abbreviation +type DAngle = DPlaneAngle -- Abbreviation + +type Thrust = Force +type DThrust = DForce + +type Torque = MomentOfForce +type DTorque = DMomentOfForce + +type EnergyPerUnitMass = SpecificEnergy +type DEnergyPerUnitMass = DSpecificEnergy + +{- $powers-of-length-units +It is permissible to express powers of length units by prefixing +'square' and 'cubic' (see section 9.6 "Spelling unit names raised +to powers" of <#note1 [1]>). + +These definitions may seem slightly out of place but these is no +obvious place where they should be. Here they are at least close +to the definitions of 'DArea' and 'DVolume'. +-} + +-- $setup +-- >>> import Numeric.Units.Dimensional.Prelude + +-- | Constructs a unit of area from a unit of length, taking the area of a square whose sides are that length. +-- +-- >>> 64 *~ square meter == (8 *~ meter) ^ pos2 +-- True +square :: (Fractional a, Typeable m) => Unit m DLength a -> Unit 'NonMetric DArea a +square x = x ^ pos2 + +-- | Constructs a unit of volume from a unit of length, taking the volume of a cube whose sides are that length. +-- +-- >>> 64 *~ cubic meter == (4 *~ meter) ^ pos3 +-- True +cubic :: (Fractional a, Typeable m) => Unit m DLength a -> Unit 'NonMetric DVolume a +cubic x = x ^ pos3
src/Numeric/Units/Dimensional/SIUnits.hs view
@@ -1,315 +1,315 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE NumDecimals #-}-{-# LANGUAGE RankNTypes #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--= Summary--This module defines the SI prefixes, the SI base units and the SI-derived units. It also defines the units outside of the SI that are-accepted for use with the SI. Any chapters, sections or tables-referenced are from <#note1 [1]> unless otherwise specified.--= References--1. #note1# http://physics.nist.gov/Pubs/SP811/-2. #note2# http://en.wikipedia.org/wiki/Minute_of_arc-3. #note3# http://en.wikipedia.org/wiki/Astronomical_unit---}--module Numeric.Units.Dimensional.SIUnits-(- -- * SI Base Units- -- $base-units- metre, meter, gram, second, ampere, kelvin, mole, candela,- -- * SI Derived Units- -- $derived-units- radian, steradian, hertz, newton, pascal, joule, watt, coulomb, volt, farad, ohm, siemens, weber, tesla, henry, lumen, lux,- -- ** Celsius Temperature- -- $celsius- degreeCelsius, fromDegreeCelsiusAbsolute, toDegreeCelsiusAbsolute,- -- ** Units Admitted for Reasons of Safeguarding Human Health- -- $health- becquerel, gray, sievert, katal,- -- * Units Accepted for Use with the SI- -- $accepted-units- minute, hour, day,- hectare, litre, liter, tonne, metricTon,- -- ** Units of Plane Angle- -- $arc-units- degree, arcminute, arcsecond,- -- $arc-units-alternate- degreeOfArc, minuteOfArc, secondOfArc,- -- ** Units Formerly Defined By Experiment- -- $values-obtained-experimentally- astronomicalUnit,- -- * SI Prefixes- -- $multiples- deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta,- -- $submultiples- deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto,- -- $reified-prefixes- Prefix, applyPrefix, siPrefixes-)-where--import Data.Ratio-import Numeric.Units.Dimensional-import Numeric.Units.Dimensional.Quantities-import Numeric.Units.Dimensional.UnitNames (Prefix, siPrefixes)-import qualified Numeric.Units.Dimensional.UnitNames as N-import Numeric.Units.Dimensional.UnitNames.Internal (ucum, ucumMetric)-import qualified Numeric.Units.Dimensional.UnitNames.Internal as I-import Numeric.NumType.DK.Integers ( pos3 )-import Prelude ( Eq(..), ($), Num, Fractional, Floating, otherwise, error)-import qualified Prelude--{- $multiples-Prefixes are used to form decimal multiples and submultiples of SI-Units as described in section 4.4. We will define the SI prefixes-in terms of the 'prefix' function which applies a scale factor to a-unit.--By defining SI prefixes as functions applied to a 'Unit' we satisfy-section 6.2.6 "Unacceptability of stand-alone prefixes".--We define all SI prefixes from Table 5. Multiples first.--}--applyMultiple :: (Num a) => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a-applyMultiple p u | denominator x == 1 = mkUnitZ n' (numerator x) u- | otherwise = error "Attempt to apply a submultiple prefix as a multiple."- where- n' = N.applyPrefix p (name u)- x = N.scaleFactor p--deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta- :: Num a => Unit 'Metric d a -> Unit 'NonMetric d a-deka = applyMultiple I.deka -- International English.-deca = deka -- American English.-hecto = applyMultiple I.hecto-kilo = applyMultiple I.kilo-mega = applyMultiple I.mega-giga = applyMultiple I.giga-tera = applyMultiple I.tera-peta = applyMultiple I.peta-exa = applyMultiple I.exa-zetta = applyMultiple I.zetta-yotta = applyMultiple I.yotta--{- $submultiples-Then the submultiples.--}--applyPrefix :: (Fractional a) => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a-applyPrefix p u = mkUnitQ n' x u- where- n' = N.applyPrefix p (name u)- x = N.scaleFactor p--deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto- :: Fractional a => Unit 'Metric d a -> Unit 'NonMetric d a-deci = applyPrefix I.deci-centi = applyPrefix I.centi-milli = applyPrefix I.milli-micro = applyPrefix I.micro-nano = applyPrefix I.nano-pico = applyPrefix I.pico-femto = applyPrefix I.femto-atto = applyPrefix I.atto-zepto = applyPrefix I.zepto-yocto = applyPrefix I.yocto--{- $reified-prefixes--We supply an explicit representation of an SI prefix, along with a function to apply one and a-list of all prefixes defined by the SI.---}--{- $base-units-These are the base units from section 4.1. To avoid a-myriad of one-letter functions that would doubtlessly cause clashes-and frustration in users' code we spell out all unit names in full,-as we did for prefixes. We also elect to spell the unit names in-singular form, as allowed by section 9.7 "Other spelling conventions".--We define the SI base units in the order of table 1.--}--metre, meter :: Num a => Unit 'Metric DLength a-metre = mkUnitZ I.nMeter 1 siUnit -- International English.-meter = metre -- American English.--{---For mass the SI base unit is kilogram. For sensible prefixes we-define gram here (see section 6.2.7 "Prefixes and the kilogram").-The drawback is that we are forced to use 'Fractional'.---}--gram :: Fractional a => Unit 'Metric DMass a-gram = mkUnitQ I.nGram 1e-3 siUnit-second :: Num a => Unit 'Metric DTime a-second = mkUnitZ I.nSecond 1 siUnit-ampere :: Num a => Unit 'Metric DElectricCurrent a-ampere = mkUnitZ I.nAmpere 1 siUnit-kelvin :: Num a => Unit 'Metric DThermodynamicTemperature a-kelvin = mkUnitZ I.nKelvin 1 siUnit-mole :: Num a => Unit 'Metric DAmountOfSubstance a-mole = mkUnitZ I.nMole 1 siUnit-candela :: Num a => Unit 'Metric DLuminousIntensity a-candela = mkUnitZ I.nCandela 1 siUnit--{- $derived-units-From Table 3, SI derived units with special names and symbols, including the-radian and steradian.--}--radian :: Num a => Unit 'Metric DPlaneAngle a-radian = mkUnitZ (ucumMetric "rad" "rad" "radian") 1 siUnit -- meter * meter ^ neg1-steradian :: Num a => Unit 'Metric DSolidAngle a-steradian = mkUnitZ (ucumMetric "sr" "sr" "steradian") 1 siUnit -- meter ^ pos2 * meter ^ neg2-hertz :: Num a => Unit 'Metric DFrequency a-hertz = mkUnitZ (ucumMetric "Hz" "Hz" "Hertz") 1 $ siUnit-newton :: Num a => Unit 'Metric DForce a-newton = mkUnitZ (ucumMetric "N" "N" "Newton") 1 $ siUnit-pascal :: Num a => Unit 'Metric DPressure a-pascal = mkUnitZ (ucumMetric "Pa" "Pa" "Pascal") 1 $ siUnit-joule :: Num a => Unit 'Metric DEnergy a-joule = mkUnitZ (ucumMetric "J" "J" "Joule") 1 $ siUnit-watt :: Num a => Unit 'Metric DPower a-watt = mkUnitZ (ucumMetric "W" "W" "Watt") 1 $ siUnit-coulomb :: Num a => Unit 'Metric DElectricCharge a-coulomb = mkUnitZ (ucumMetric "C" "C" "Coulomb") 1 $ siUnit-volt :: Num a => Unit 'Metric DElectricPotential a-volt = mkUnitZ (ucumMetric "V" "V" "Volt") 1 $ siUnit-farad :: Num a => Unit 'Metric DCapacitance a-farad = mkUnitZ (ucumMetric "F" "F" "Farad") 1 $ siUnit-ohm :: Num a => Unit 'Metric DElectricResistance a-ohm = mkUnitZ (ucumMetric "Ohm" "Ω" "Ohm") 1 $ siUnit-siemens :: Num a => Unit 'Metric DElectricConductance a-siemens = mkUnitZ (ucumMetric "S" "S" "Siemens") 1 $ siUnit-weber :: Num a => Unit 'Metric DMagneticFlux a-weber = mkUnitZ (ucumMetric "Wb" "Wb" "Weber") 1 $ siUnit-tesla :: Num a => Unit 'Metric DMagneticFluxDensity a-tesla = mkUnitZ (ucumMetric "T" "T" "Tesla") 1 $ siUnit-henry :: Num a => Unit 'Metric DInductance a-henry = mkUnitZ (ucumMetric "H" "H" "Henry") 1 $ siUnit--{--We defer the definition of Celcius temperature to another section (would-appear here if we stricly followed table 3).--}--lumen :: Num a => Unit 'Metric DLuminousFlux a-lumen = mkUnitZ (ucumMetric "lm" "lm" "lumen") 1 $ siUnit-lux :: Num a => Unit 'Metric DIlluminance a-lux = mkUnitZ (ucumMetric "lx" "lx" "lux") 1 $ siUnit--{- $celsius-A problematic area is units which increase proportionally to the-base SI units but cross zero at a different point. An example would-be degrees Celsius (see section 4.2.1.1). The author feels that it-is appropriate to define a unit for use with relative quantities-(taking only into account the proportionality) and complement the-unit with functions for converting absolute values.--The function 'fromDegreeCelsiusAbsolute' should be used in lieu of-"*~ degreeCelsius" when working with absolute temperatures. Similarily,-'toDegreeCelsiusAbsolute' should be used in lieu of "/~ degreeCelsius"-when working with absolute temperatures.--}--degreeCelsius :: Num a => Unit 'Metric DCelsiusTemperature a-degreeCelsius = kelvin--fromDegreeCelsiusAbsolute :: Floating a => a -> ThermodynamicTemperature a-fromDegreeCelsiusAbsolute x = x *~ degreeCelsius + 273.15 *~ degreeCelsius-toDegreeCelsiusAbsolute :: Floating a => ThermodynamicTemperature a -> a-toDegreeCelsiusAbsolute x = (x - 273.15 *~ degreeCelsius) /~ degreeCelsius--{- $health--The last units from Table 3 are SI derived units with special names and symbols admitted for reasons-of safeguarding human health.--}--becquerel :: Num a => Unit 'Metric DActivity a-becquerel = mkUnitZ (ucumMetric "Bq" "Bq" "Becquerel") 1 $ siUnit-gray :: Num a => Unit 'Metric DAbsorbedDose a-gray = mkUnitZ (ucumMetric "Gy" "Gy" "Gray") 1 $ siUnit-sievert :: Num a => Unit 'Metric DDoseEquivalent a-sievert = mkUnitZ (ucumMetric "Sv" "Sv" "Sievert") 1 $ siUnit-katal :: Num a => Unit 'Metric DCatalyticActivity a-katal = mkUnitZ (ucumMetric "kat" "kat" "katal") 1 $ siUnit--{- $accepted-units-There are several units that are not strictly part of the SI but-are either permanently or temporarily accepted for use with the SI.-We define the permanently accepted ones in this module.--From Table 6, Units accepted for use with the SI.--We start with time which we grant exclusive rights to 'minute' and-'second'.--}-minute, hour, day :: Num a => Unit 'NonMetric DTime a-minute = mkUnitZ (ucum "min" "min" "minute") 60 $ second-hour = mkUnitZ (ucum "h" "h" "hour") 60 $ minute-day = mkUnitZ (ucum "d" "d" "day") 24 $ hour -- Mean solar day.--{- $arc-units--Since 'minute' and 'second' are already in use for time we use-'arcminute' and 'arcsecond' <#note2 [2]> for plane angle instead.--}--degree, arcminute, arcsecond :: Floating a => Unit 'NonMetric DPlaneAngle a-degree = mkUnitR (ucum "deg" "°" "degree") (Prelude.pi Prelude./ 180) $ radian-arcminute = mkUnitR (ucum "'" "'" "arcminute") (Prelude.recip 60) $ degreeOfArc-arcsecond = mkUnitR (ucum "''" "''" "arcsecond") (Prelude.recip 60) $ minuteOfArc--{- $arc-units-alternate-Alternate (longer) forms of the above. In particular 'degreeOfArc'-can be used if there is a percieved need to disambiguate from e.g.-temperature.--}--degreeOfArc, minuteOfArc, secondOfArc :: Floating a => Unit 'NonMetric DPlaneAngle a-degreeOfArc = degree-secondOfArc = arcsecond-minuteOfArc = arcminute--hectare :: Fractional a => Unit 'NonMetric DArea a-hectare = square (hecto meter)--litre, liter :: Fractional a => Unit 'Metric DVolume a-litre = mkUnitQ (ucumMetric "L" "L" "litre") 1 $ deci meter ^ pos3 -- International English.-liter = litre -- American English.--tonne, metricTon :: Num a => Unit 'Metric DMass a-tonne = mkUnitZ (ucumMetric "t" "t" "tonne") 1000 $ siUnit -- Name in original SI text.-metricTon = tonne -- American name.--{- $values-obtained-experimentally-We decline to provide here those units - listed in Table 7 - which,-while accepted for use with the SI, have values which are determined experimentally.-For versioning purposes, those units can be found in "Numeric.Units.Dimensional.NonSI".--However, in 2012 the IAU redefined the astronomical unit as a conventional-unit of length directly tied to the meter, with a length of exactly-149 597 870 700 m and the official abbreviation of au <#note3 [3]>. We therefore include it here.--}--astronomicalUnit :: Num a => Unit 'NonMetric DLength a-astronomicalUnit = mkUnitZ (ucum "AU" "AU" "astronomical unit") 149597870700 $ meter+{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NumDecimals #-} +{-# LANGUAGE RankNTypes #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + += Summary + +This module defines the SI prefixes, the SI base units and the SI +derived units. It also defines the units outside of the SI that are +accepted for use with the SI. Any chapters, sections or tables +referenced are from <#note1 [1]> unless otherwise specified. + += References + +1. #note1# http://physics.nist.gov/Pubs/SP811/ +2. #note2# http://en.wikipedia.org/wiki/Minute_of_arc +3. #note3# http://en.wikipedia.org/wiki/Astronomical_unit + +-} + +module Numeric.Units.Dimensional.SIUnits +( + -- * SI Base Units + -- $base-units + metre, meter, gram, second, ampere, kelvin, mole, candela, + -- * SI Derived Units + -- $derived-units + radian, steradian, hertz, newton, pascal, joule, watt, coulomb, volt, farad, ohm, siemens, weber, tesla, henry, lumen, lux, + -- ** Celsius Temperature + -- $celsius + degreeCelsius, fromDegreeCelsiusAbsolute, toDegreeCelsiusAbsolute, + -- ** Units Admitted for Reasons of Safeguarding Human Health + -- $health + becquerel, gray, sievert, katal, + -- * Units Accepted for Use with the SI + -- $accepted-units + minute, hour, day, + hectare, litre, liter, tonne, metricTon, + -- ** Units of Plane Angle + -- $arc-units + degree, arcminute, arcsecond, + -- $arc-units-alternate + degreeOfArc, minuteOfArc, secondOfArc, + -- ** Units Formerly Defined By Experiment + -- $values-obtained-experimentally + astronomicalUnit, + -- * SI Prefixes + -- $multiples + deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta, + -- $submultiples + deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto, + -- $reified-prefixes + Prefix, applyPrefix, siPrefixes +) +where + +import Data.Ratio +import Numeric.Units.Dimensional +import Numeric.Units.Dimensional.Quantities +import Numeric.Units.Dimensional.UnitNames (Prefix, siPrefixes) +import qualified Numeric.Units.Dimensional.UnitNames as N +import Numeric.Units.Dimensional.UnitNames.Internal (ucum, ucumMetric) +import qualified Numeric.Units.Dimensional.UnitNames.Internal as I +import Numeric.NumType.DK.Integers ( pos3 ) +import Prelude ( Eq(..), ($), Num, Fractional, Floating, otherwise, error) +import qualified Prelude + +{- $multiples +Prefixes are used to form decimal multiples and submultiples of SI +Units as described in section 4.4. We will define the SI prefixes +in terms of the 'prefix' function which applies a scale factor to a +unit. + +By defining SI prefixes as functions applied to a 'Unit' we satisfy +section 6.2.6 "Unacceptability of stand-alone prefixes". + +We define all SI prefixes from Table 5. Multiples first. +-} + +applyMultiple :: (Num a) => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a +applyMultiple p u | denominator x == 1 = mkUnitZ n' (numerator x) u + | otherwise = error "Attempt to apply a submultiple prefix as a multiple." + where + n' = N.applyPrefix p (name u) + x = N.scaleFactor p + +deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta + :: Num a => Unit 'Metric d a -> Unit 'NonMetric d a +deka = applyMultiple I.deka -- International English. +deca = deka -- American English. +hecto = applyMultiple I.hecto +kilo = applyMultiple I.kilo +mega = applyMultiple I.mega +giga = applyMultiple I.giga +tera = applyMultiple I.tera +peta = applyMultiple I.peta +exa = applyMultiple I.exa +zetta = applyMultiple I.zetta +yotta = applyMultiple I.yotta + +{- $submultiples +Then the submultiples. +-} + +applyPrefix :: (Fractional a) => Prefix -> Unit 'Metric d a -> Unit 'NonMetric d a +applyPrefix p u = mkUnitQ n' x u + where + n' = N.applyPrefix p (name u) + x = N.scaleFactor p + +deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto + :: Fractional a => Unit 'Metric d a -> Unit 'NonMetric d a +deci = applyPrefix I.deci +centi = applyPrefix I.centi +milli = applyPrefix I.milli +micro = applyPrefix I.micro +nano = applyPrefix I.nano +pico = applyPrefix I.pico +femto = applyPrefix I.femto +atto = applyPrefix I.atto +zepto = applyPrefix I.zepto +yocto = applyPrefix I.yocto + +{- $reified-prefixes + +We supply an explicit representation of an SI prefix, along with a function to apply one and a +list of all prefixes defined by the SI. + +-} + +{- $base-units +These are the base units from section 4.1. To avoid a +myriad of one-letter functions that would doubtlessly cause clashes +and frustration in users' code we spell out all unit names in full, +as we did for prefixes. We also elect to spell the unit names in +singular form, as allowed by section 9.7 "Other spelling conventions". + +We define the SI base units in the order of table 1. +-} + +metre, meter :: Num a => Unit 'Metric DLength a +metre = mkUnitZ I.nMeter 1 siUnit -- International English. +meter = metre -- American English. + +{- + +For mass the SI base unit is kilogram. For sensible prefixes we +define gram here (see section 6.2.7 "Prefixes and the kilogram"). +The drawback is that we are forced to use 'Fractional'. + +-} + +gram :: Fractional a => Unit 'Metric DMass a +gram = mkUnitQ I.nGram 1e-3 siUnit +second :: Num a => Unit 'Metric DTime a +second = mkUnitZ I.nSecond 1 siUnit +ampere :: Num a => Unit 'Metric DElectricCurrent a +ampere = mkUnitZ I.nAmpere 1 siUnit +kelvin :: Num a => Unit 'Metric DThermodynamicTemperature a +kelvin = mkUnitZ I.nKelvin 1 siUnit +mole :: Num a => Unit 'Metric DAmountOfSubstance a +mole = mkUnitZ I.nMole 1 siUnit +candela :: Num a => Unit 'Metric DLuminousIntensity a +candela = mkUnitZ I.nCandela 1 siUnit + +{- $derived-units +From Table 3, SI derived units with special names and symbols, including the +radian and steradian. +-} + +radian :: Num a => Unit 'Metric DPlaneAngle a +radian = mkUnitZ (ucumMetric "rad" "rad" "radian") 1 siUnit -- meter * meter ^ neg1 +steradian :: Num a => Unit 'Metric DSolidAngle a +steradian = mkUnitZ (ucumMetric "sr" "sr" "steradian") 1 siUnit -- meter ^ pos2 * meter ^ neg2 +hertz :: Num a => Unit 'Metric DFrequency a +hertz = mkUnitZ (ucumMetric "Hz" "Hz" "Hertz") 1 $ siUnit +newton :: Num a => Unit 'Metric DForce a +newton = mkUnitZ (ucumMetric "N" "N" "Newton") 1 $ siUnit +pascal :: Num a => Unit 'Metric DPressure a +pascal = mkUnitZ (ucumMetric "Pa" "Pa" "Pascal") 1 $ siUnit +joule :: Num a => Unit 'Metric DEnergy a +joule = mkUnitZ (ucumMetric "J" "J" "Joule") 1 $ siUnit +watt :: Num a => Unit 'Metric DPower a +watt = mkUnitZ (ucumMetric "W" "W" "Watt") 1 $ siUnit +coulomb :: Num a => Unit 'Metric DElectricCharge a +coulomb = mkUnitZ (ucumMetric "C" "C" "Coulomb") 1 $ siUnit +volt :: Num a => Unit 'Metric DElectricPotential a +volt = mkUnitZ (ucumMetric "V" "V" "Volt") 1 $ siUnit +farad :: Num a => Unit 'Metric DCapacitance a +farad = mkUnitZ (ucumMetric "F" "F" "Farad") 1 $ siUnit +ohm :: Num a => Unit 'Metric DElectricResistance a +ohm = mkUnitZ (ucumMetric "Ohm" "Ω" "Ohm") 1 $ siUnit +siemens :: Num a => Unit 'Metric DElectricConductance a +siemens = mkUnitZ (ucumMetric "S" "S" "Siemens") 1 $ siUnit +weber :: Num a => Unit 'Metric DMagneticFlux a +weber = mkUnitZ (ucumMetric "Wb" "Wb" "Weber") 1 $ siUnit +tesla :: Num a => Unit 'Metric DMagneticFluxDensity a +tesla = mkUnitZ (ucumMetric "T" "T" "Tesla") 1 $ siUnit +henry :: Num a => Unit 'Metric DInductance a +henry = mkUnitZ (ucumMetric "H" "H" "Henry") 1 $ siUnit + +{- +We defer the definition of Celcius temperature to another section (would +appear here if we stricly followed table 3). +-} + +lumen :: Num a => Unit 'Metric DLuminousFlux a +lumen = mkUnitZ (ucumMetric "lm" "lm" "lumen") 1 $ siUnit +lux :: Num a => Unit 'Metric DIlluminance a +lux = mkUnitZ (ucumMetric "lx" "lx" "lux") 1 $ siUnit + +{- $celsius +A problematic area is units which increase proportionally to the +base SI units but cross zero at a different point. An example would +be degrees Celsius (see section 4.2.1.1). The author feels that it +is appropriate to define a unit for use with relative quantities +(taking only into account the proportionality) and complement the +unit with functions for converting absolute values. + +The function 'fromDegreeCelsiusAbsolute' should be used in lieu of +"*~ degreeCelsius" when working with absolute temperatures. Similarily, +'toDegreeCelsiusAbsolute' should be used in lieu of "/~ degreeCelsius" +when working with absolute temperatures. +-} + +degreeCelsius :: Num a => Unit 'Metric DCelsiusTemperature a +degreeCelsius = kelvin + +fromDegreeCelsiusAbsolute :: Floating a => a -> ThermodynamicTemperature a +fromDegreeCelsiusAbsolute x = x *~ degreeCelsius + 273.15 *~ degreeCelsius +toDegreeCelsiusAbsolute :: Floating a => ThermodynamicTemperature a -> a +toDegreeCelsiusAbsolute x = (x - 273.15 *~ degreeCelsius) /~ degreeCelsius + +{- $health + +The last units from Table 3 are SI derived units with special names and symbols admitted for reasons +of safeguarding human health. +-} + +becquerel :: Num a => Unit 'Metric DActivity a +becquerel = mkUnitZ (ucumMetric "Bq" "Bq" "Becquerel") 1 $ siUnit +gray :: Num a => Unit 'Metric DAbsorbedDose a +gray = mkUnitZ (ucumMetric "Gy" "Gy" "Gray") 1 $ siUnit +sievert :: Num a => Unit 'Metric DDoseEquivalent a +sievert = mkUnitZ (ucumMetric "Sv" "Sv" "Sievert") 1 $ siUnit +katal :: Num a => Unit 'Metric DCatalyticActivity a +katal = mkUnitZ (ucumMetric "kat" "kat" "katal") 1 $ siUnit + +{- $accepted-units +There are several units that are not strictly part of the SI but +are either permanently or temporarily accepted for use with the SI. +We define the permanently accepted ones in this module. + +From Table 6, Units accepted for use with the SI. + +We start with time which we grant exclusive rights to 'minute' and +'second'. +-} +minute, hour, day :: Num a => Unit 'NonMetric DTime a +minute = mkUnitZ (ucum "min" "min" "minute") 60 $ second +hour = mkUnitZ (ucum "h" "h" "hour") 60 $ minute +day = mkUnitZ (ucum "d" "d" "day") 24 $ hour -- Mean solar day. + +{- $arc-units + +Since 'minute' and 'second' are already in use for time we use +'arcminute' and 'arcsecond' <#note2 [2]> for plane angle instead. +-} + +degree, arcminute, arcsecond :: Floating a => Unit 'NonMetric DPlaneAngle a +degree = mkUnitR (ucum "deg" "°" "degree") (Prelude.pi Prelude./ 180) $ radian +arcminute = mkUnitR (ucum "'" "'" "arcminute") (Prelude.recip 60) $ degreeOfArc +arcsecond = mkUnitR (ucum "''" "''" "arcsecond") (Prelude.recip 60) $ minuteOfArc + +{- $arc-units-alternate +Alternate (longer) forms of the above. In particular 'degreeOfArc' +can be used if there is a percieved need to disambiguate from e.g. +temperature. +-} + +degreeOfArc, minuteOfArc, secondOfArc :: Floating a => Unit 'NonMetric DPlaneAngle a +degreeOfArc = degree +secondOfArc = arcsecond +minuteOfArc = arcminute + +hectare :: Fractional a => Unit 'NonMetric DArea a +hectare = square (hecto meter) + +litre, liter :: Fractional a => Unit 'Metric DVolume a +litre = mkUnitQ (ucumMetric "L" "L" "litre") 1 $ deci meter ^ pos3 -- International English. +liter = litre -- American English. + +tonne, metricTon :: Num a => Unit 'Metric DMass a +tonne = mkUnitZ (ucumMetric "t" "t" "tonne") 1000 $ siUnit -- Name in original SI text. +metricTon = tonne -- American name. + +{- $values-obtained-experimentally +We decline to provide here those units - listed in Table 7 - which, +while accepted for use with the SI, have values which are determined experimentally. +For versioning purposes, those units can be found in "Numeric.Units.Dimensional.NonSI". + +However, in 2012 the IAU redefined the astronomical unit as a conventional +unit of length directly tied to the meter, with a length of exactly +149 597 870 700 m and the official abbreviation of au <#note3 [3]>. We therefore include it here. +-} + +astronomicalUnit :: Num a => Unit 'NonMetric DLength a +astronomicalUnit = mkUnitZ (ucum "AU" "AU" "astronomical unit") 149597870700 $ meter
src/Numeric/Units/Dimensional/UnitNames.hs view
@@ -1,37 +1,37 @@-{-# LANGUAGE PatternSynonyms #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--This module provides types and functions for manipulating unit names.--Please note that the details of the name representation may be less stable than the other APIs-provided by this package, as new features using them are still being developed.---}-module Numeric.Units.Dimensional.UnitNames-(- -- * Data Types- UnitName, NameAtom, Prefix, PrefixName, Metricality(..),- -- * Construction of Unit Names- atom, applyPrefix, (*), (/), (^), product, reduce, grouped,- -- * Standard Names- baseUnitName, siPrefixes, nOne,- -- * Inspecting Prefixes- prefixName, scaleFactor,- -- * Convenience Type Synonyms for Unit Name Transformations- UnitNameTransformer, UnitNameTransformer2,- -- * Forgetting Unwanted Phantom Types- weaken, strengthen, relax,- name_en, abbreviation_en, asAtomic-)-where--import Numeric.Units.Dimensional.UnitNames.Internal-import Numeric.Units.Dimensional.Variants-import Prelude hiding ((*), (/), (^), product)+{-# LANGUAGE PatternSynonyms #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +This module provides types and functions for manipulating unit names. + +Please note that the details of the name representation may be less stable than the other APIs +provided by this package, as new features using them are still being developed. + +-} +module Numeric.Units.Dimensional.UnitNames +( + -- * Data Types + UnitName, NameAtom, Prefix, PrefixName, Metricality(..), + -- * Construction of Unit Names + atom, applyPrefix, (*), (/), (^), product, reduce, grouped, + -- * Standard Names + baseUnitName, siPrefixes, nOne, + -- * Inspecting Prefixes + prefixName, scaleFactor, + -- * Convenience Type Synonyms for Unit Name Transformations + UnitNameTransformer, UnitNameTransformer2, + -- * Forgetting Unwanted Phantom Types + weaken, strengthen, relax, + name_en, abbreviation_en, asAtomic +) +where + +import Numeric.Units.Dimensional.UnitNames.Internal +import Numeric.Units.Dimensional.Variants +import Prelude hiding ((*), (/), (^), product)
src/Numeric/Units/Dimensional/UnitNames/InterchangeNames.hs view
@@ -1,41 +1,41 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Numeric.Units.Dimensional.UnitNames.InterchangeNames-(- InterchangeNameAuthority(..),- InterchangeName(..),- HasInterchangeName(..)-)-where--import Control.DeepSeq-import Data.Data-import GHC.Generics-import Prelude---- | Represents the authority which issued an interchange name for a unit.-data InterchangeNameAuthority = UCUM -- ^ The interchange name originated with the Unified Code for Units of Measure.- | DimensionalLibrary -- ^ The interchange name originated with the dimensional library.- | Custom -- ^ The interchange name originated with a user of the dimensional library.- deriving (Eq, Ord, Show, Data, Typeable, Generic)--instance NFData InterchangeNameAuthority where -- instance is derived from Generic instance--data InterchangeName = InterchangeName { name :: String, authority :: InterchangeNameAuthority, isAtomic :: Bool }- deriving (Eq, Ord, Data, Typeable, Generic)--instance NFData InterchangeName where -- instance is derived from Generic instance--instance Show InterchangeName where- show n = name n ++ " (Issued by " ++ show (authority n) ++ ")"---- | Determines the authority which issued the interchange name of a unit or unit name.--- For composite units, this is the least-authoritative interchange name of any constituent name.------ Note that the least-authoritative authority is the one sorted as greatest by the 'Ord' instance of 'InterchangeNameAuthority'.-class HasInterchangeName a where- interchangeName :: a -> InterchangeName--instance HasInterchangeName InterchangeName where- interchangeName = id+{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} + +module Numeric.Units.Dimensional.UnitNames.InterchangeNames +( + InterchangeNameAuthority(..), + InterchangeName(..), + HasInterchangeName(..) +) +where + +import Control.DeepSeq +import Data.Data +import GHC.Generics +import Prelude + +-- | Represents the authority which issued an interchange name for a unit. +data InterchangeNameAuthority = UCUM -- ^ The interchange name originated with the Unified Code for Units of Measure. + | DimensionalLibrary -- ^ The interchange name originated with the dimensional library. + | Custom -- ^ The interchange name originated with a user of the dimensional library. + deriving (Eq, Ord, Show, Data, Typeable, Generic) + +instance NFData InterchangeNameAuthority where -- instance is derived from Generic instance + +data InterchangeName = InterchangeName { name :: String, authority :: InterchangeNameAuthority, isAtomic :: Bool } + deriving (Eq, Ord, Data, Typeable, Generic) + +instance NFData InterchangeName where -- instance is derived from Generic instance + +instance Show InterchangeName where + show n = name n ++ " (Issued by " ++ show (authority n) ++ ")" + +-- | Determines the authority which issued the interchange name of a unit or unit name. +-- For composite units, this is the least-authoritative interchange name of any constituent name. +-- +-- Note that the least-authoritative authority is the one sorted as greatest by the 'Ord' instance of 'InterchangeNameAuthority'. +class HasInterchangeName a where + interchangeName :: a -> InterchangeName + +instance HasInterchangeName InterchangeName where + interchangeName = id
src/Numeric/Units/Dimensional/UnitNames/Internal.hs view
@@ -1,361 +1,361 @@-{-# OPTIONS_HADDOCK not-home #-}--{-# LANGUAGE AutoDeriveTypeable #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE NumDecimals #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}--module Numeric.Units.Dimensional.UnitNames.Internal-where--import Control.DeepSeq-import Control.Monad (join)-import Data.Coerce-import Data.Data hiding (Prefix)-#if MIN_VERSION_base(4, 8, 0)-import Data.Foldable (toList)-#else-import Data.Foldable (Foldable, toList)-#endif-import Data.Ord-import GHC.Generics hiding (Prefix)-import Numeric.Units.Dimensional.Dimensions.TermLevel (Dimension', asList, HasDimension(..))-import Numeric.Units.Dimensional.UnitNames.InterchangeNames hiding (isAtomic)-import qualified Numeric.Units.Dimensional.UnitNames.InterchangeNames as I-import Numeric.Units.Dimensional.Variants (Metricality(..))-import Prelude hiding ((*), (/), (^), product)-import qualified Prelude as P---- | The name of a unit.-data UnitName (m :: Metricality) where- -- The name of the unit of dimensionless values.- One :: UnitName 'NonMetric- -- A name of an atomic unit to which metric prefixes may be applied.- MetricAtomic :: NameAtom ('UnitAtom 'Metric) -> UnitName 'Metric- -- A name of an atomic unit to which metric prefixes may not be applied.- Atomic :: NameAtom ('UnitAtom 'NonMetric) -> UnitName 'NonMetric- -- A name of a prefixed unit.- Prefixed :: PrefixName -> UnitName 'Metric -> UnitName 'NonMetric- -- A compound name formed from the product of two names.- Product :: UnitName 'NonMetric -> UnitName 'NonMetric -> UnitName 'NonMetric- -- A compound name formed from the quotient of two names.- Quotient :: UnitName 'NonMetric -> UnitName 'NonMetric -> UnitName 'NonMetric- -- A compound name formed by raising a unit name to an integer power.- Power :: UnitName 'NonMetric -> Int -> UnitName 'NonMetric- -- A compound name formed by grouping another name, which is generally compound.- Grouped :: UnitName 'NonMetric -> UnitName 'NonMetric- -- A weakened name formed by forgetting that it could accept a metric prefix.- --- -- Also available is the smart constructor `weaken` which accepts any `UnitName` as input.- Weaken :: UnitName 'Metric -> UnitName 'NonMetric- deriving (Typeable)--deriving instance Eq (UnitName m)---- As it is for a GADT, this instance cannot be derived or use the generic default implementation-instance NFData (UnitName m) where- rnf n = case n of- One -> ()- MetricAtomic a -> rnf a- Atomic a -> rnf a- Prefixed p n' -> rnf p `seq` rnf n'- Product n1 n2 -> rnf n1 `seq` rnf n2- Quotient n1 n2 -> rnf n1 `seq` rnf n2- Power n' e -> rnf n' `seq` rnf e- Grouped n' -> rnf n'- Weaken n' -> rnf n'--instance Show (UnitName m) where- show One = "1"- show (MetricAtomic a) = abbreviation_en a- show (Atomic a) = abbreviation_en a- show (Prefixed a n) = abbreviation_en a ++ show n- show (Product n1 n2) = show n1 ++ " " ++ show n2- show (Quotient n1 n2) = show n1 ++ " / " ++ show n2- show (Power x n) = show x ++ "^" ++ show n- show (Grouped n) = "(" ++ show n ++ ")"- show (Weaken n) = show n--asAtomic :: UnitName m -> Maybe (NameAtom ('UnitAtom m))-asAtomic (MetricAtomic a) = Just a-asAtomic (Atomic a) = Just a-asAtomic (Weaken n) = fmap coerce $ asAtomic n-asAtomic _ = Nothing--isAtomic :: UnitName m -> Bool-isAtomic (One) = True-isAtomic (MetricAtomic _) = True-isAtomic (Atomic _) = True-isAtomic (Prefixed _ _) = True-isAtomic (Grouped _) = True-isAtomic (Weaken n) = isAtomic n-isAtomic _ = False--isAtomicOrProduct :: UnitName m -> Bool-isAtomicOrProduct (Product _ _) = True-isAtomicOrProduct n = isAtomic n---- reduce by algebraic simplifications-reduce :: UnitName m -> UnitName m-reduce (One) = One-reduce n@(MetricAtomic _) = n-reduce n@(Atomic _) = n-reduce n@(Prefixed _ _) = n-reduce (Product n1 n2) = reduce' (reduce n1 * reduce n2)-reduce (Quotient n1 n2) = reduce' (reduce n1 * reduce n2)-reduce (Power n x) = reduce' ((reduce n) ^ x)-reduce (Grouped n) = reduce' (Grouped (reduce n))-reduce (Weaken n) = reduce' (Weaken (reduce n))---- reduce, knowing that subterms are already in reduced form-reduce' :: UnitName m -> UnitName m-reduce' (Product One n) = reduce' n-reduce' (Product n One) = reduce' n-reduce' (Power (Power n x1) x2) = reduce (n ^ (x1 P.* x2))-reduce' (Power (Grouped (Power n x1)) x2) = reduce (n ^ (x1 P.* x2))-reduce' (Power _ 0) = One-reduce' (Power n 1) = reduce' n-reduce' (Grouped n) = reduce' n-reduce' n@(Weaken (MetricAtomic _)) = n-reduce' n = n--data NameAtomType = UnitAtom Metricality- | PrefixAtom- deriving (Eq, Ord, Data, Typeable, Generic)--instance NFData NameAtomType where -- instance is derived from Generic instance---- | The name of a metric prefix.-type PrefixName = NameAtom 'PrefixAtom--data Prefix = Prefix- {- -- | The name of a metric prefix.- prefixName :: PrefixName,- -- | The scale factor denoted by a metric prefix.- scaleFactor :: Rational- }- deriving (Eq, Data, Typeable, Generic)--instance Ord Prefix where- compare = comparing scaleFactor--instance NFData Prefix where -- instance is derived from Generic instance--instance HasInterchangeName Prefix where- interchangeName = interchangeName . prefixName---- | The name of the unit of dimensionless values.-nOne :: UnitName 'NonMetric-nOne = One--nMeter :: UnitName 'Metric-nMeter = ucumMetric "m" "m" "metre"--nGram :: UnitName 'Metric-nGram = ucumMetric "g" "g" "gram"--nKilogram :: UnitName 'NonMetric-nKilogram = applyPrefix kilo nGram--nSecond :: UnitName 'Metric-nSecond = ucumMetric "s" "s" "second"--nAmpere :: UnitName 'Metric-nAmpere = ucumMetric "A" "A" "Ampere"--nKelvin :: UnitName 'Metric-nKelvin = ucumMetric "K" "K" "Kelvin"--nMole :: UnitName 'Metric-nMole = ucumMetric "mol" "mol" "mole"--nCandela :: UnitName 'Metric-nCandela = ucumMetric "cd" "cd" "candela"---- | The name of the base unit associated with a specified dimension.-baseUnitName :: Dimension' -> UnitName 'NonMetric-baseUnitName d = let powers = asList $ dimension d- in reduce . product $ zipWith (^) baseUnitNames powers--baseUnitNames :: [UnitName 'NonMetric]-baseUnitNames = [weaken nMeter, nKilogram, weaken nSecond, weaken nAmpere, weaken nKelvin, weaken nMole, weaken nCandela]--deka, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta :: Prefix-deka = prefix "da" "da" "deka" 1e1-hecto = prefix "h" "h" "hecto" 1e2-kilo = prefix "k" "k" "kilo" 1e3-mega = prefix "M" "M" "mega" 1e6-giga = prefix "G" "G" "giga" 1e9-tera = prefix "T" "T" "tera" 1e12-peta = prefix "P" "P" "peta" 1e15-exa = prefix "E" "E" "exa" 1e18-zetta = prefix "Z" "Z" "zetta" 1e21-yotta = prefix "Y" "Y" "yotta" 1e24-deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto :: Prefix-deci = prefix "d" "d" "deci" 1e-1-centi = prefix "c" "c" "centi" 1e-2-milli = prefix "m" "m" "milli" 1e-3-micro = prefix "u" "μ" "micro" 1e-6-nano = prefix "n" "n" "nano" 1e-9-pico = prefix "p" "p" "pico" 1e-12-femto = prefix "f" "f" "femto" 1e-15-atto = prefix "a" "a" "atto" 1e-18-zepto = prefix "z" "z" "zepto" 1e-21-yocto = prefix "y" "y" "yocto" 1e-24---- | A list of all 'Prefix'es defined by the SI.-siPrefixes :: [Prefix]-siPrefixes = [yocto, zepto, atto, femto, pico, nano, micro, milli, centi, deci, deka, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta]---- | Forms a 'UnitName' from a 'Metric' name by applying a metric prefix.-applyPrefix :: Prefix -> UnitName 'Metric -> UnitName 'NonMetric-applyPrefix = Prefixed . prefixName--{--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.--}--infixr 8 ^-infixl 7 *, /---- | Form a 'UnitName' by taking the product of two others.-(*) :: UnitName m1 -> UnitName m2 -> UnitName 'NonMetric-a * b = Product (weaken a) (weaken b)---- | Form a 'UnitName' by dividing one by another.-(/) :: UnitName m1 -> UnitName m2 -> UnitName 'NonMetric-n1 / n2 | isAtomicOrProduct n1 = Quotient (weaken n1) (weaken n2)- | otherwise = Quotient (grouped n1) (weaken n2)---- | Form a 'UnitName' by raising a name to an integer power.-(^) :: UnitName m -> Int -> UnitName 'NonMetric-x ^ n | isAtomic x = Power (weaken x) n- | otherwise = Power (grouped x) n---- | Convert a 'UnitName' which may or may not be 'Metric' to one--- which is certainly 'NonMetric'.-weaken :: UnitName m -> UnitName 'NonMetric-weaken n@(MetricAtomic _) = Weaken n -- we really only need this one case and a catchall, but the typechecker can't see it-weaken n@One = n-weaken n@(Atomic _) = n-weaken n@(Prefixed _ _) = n-weaken n@(Product _ _) = n-weaken n@(Quotient _ _) = n-weaken n@(Power _ _) = n-weaken n@(Grouped _) = n-weaken n@(Weaken _) = n---- | Attempt to convert a 'UnitName' which may or may not be 'Metric' to one--- which is certainly 'Metric'.-strengthen :: UnitName m -> Maybe (UnitName 'Metric)-strengthen n@(MetricAtomic _) = Just n-strengthen (Weaken n) = strengthen n-strengthen _ = Nothing---- | Convert a 'UnitName' of one 'Metricality' into a name of another metricality by--- strengthening or weakening if neccessary. Because it may not be possible to strengthen,--- the result is returned in a 'Maybe' wrapper.-relax :: forall m1 m2.(Typeable m1, Typeable m2) => UnitName m1 -> Maybe (UnitName m2)-relax n = go (typeRep (Proxy :: Proxy m1)) (typeRep (Proxy :: Proxy m2)) n- where- metric = typeRep (Proxy :: Proxy 'Metric)- nonMetric = typeRep (Proxy :: Proxy 'NonMetric)- go :: TypeRep -> TypeRep -> UnitName m1 -> Maybe (UnitName m2)- go p1 p2 | p1 == p2 = cast- | (p1 == nonMetric) && (p2 == metric) = join . fmap gcast . strengthen- | (p1 == metric) && (p2 == nonMetric) = cast . weaken- | otherwise = error "Should be unreachable. TypeRep of an unexpected Metricality encountered."---- | Constructs a 'UnitName' by applying a grouping operation to--- another 'UnitName', which may be useful to express precedence.-grouped :: UnitName m -> UnitName 'NonMetric-grouped = Grouped . weaken---- | Represents the name of an atomic unit or prefix.-data NameAtom (m :: NameAtomType)- = NameAtom- {- _interchangeName :: InterchangeName, -- ^ The interchange name of the unit.- abbreviation_en :: String, -- ^ The abbreviated name of the unit in international English- name_en :: String -- ^ The full name of the unit in international English- }- deriving (Eq, Ord, Data, Typeable, Generic)--instance NFData (NameAtom m) where -- instance is derived from Generic instance--instance HasInterchangeName (NameAtom m) where- interchangeName = _interchangeName--instance HasInterchangeName (UnitName m) where- interchangeName One = InterchangeName { name = "1", authority = UCUM, I.isAtomic = True }- interchangeName (MetricAtomic a) = interchangeName a- interchangeName (Atomic a) = interchangeName a- interchangeName (Prefixed p n) = let n' = (name . interchangeName $ p) ++ (name . interchangeName $ n)- a' = max (authority . interchangeName $ p) (authority . interchangeName $ n)- in InterchangeName { name = n', authority = a', I.isAtomic = False }- interchangeName (Product n1 n2) = let n' = (name . interchangeName $ n1) ++ "." ++ (name . interchangeName $ n2)- a' = max (authority . interchangeName $ n1) (authority . interchangeName $ n2)- in InterchangeName { name = n', authority = a', I.isAtomic = False }- interchangeName (Quotient n1 n2) = let n' = (name . interchangeName $ n1) ++ "/" ++ (name . interchangeName $ n2)- a' = max (authority . interchangeName $ n1) (authority . interchangeName $ n2)- in InterchangeName { name = n', authority = a', I.isAtomic = False }- -- TODO #109: note in this case that the UCUM is changing their grammar to not accept exponents after- -- as a result it will become necessary to distribute the exponentiation over the items in the base name- -- prior to generating the interchange name- interchangeName (Power n x) = let n' = (name . interchangeName $ n) ++ (show x)- in InterchangeName { name = n', authority = authority . interchangeName $ n, I.isAtomic = False }- interchangeName (Grouped n) = let n' = "(" ++ (name . interchangeName $ n) ++ ")"- in InterchangeName { name = n', authority = authority . interchangeName $ n, I.isAtomic = False }- interchangeName (Weaken n) = interchangeName n--prefix :: String -> String -> String -> Rational -> Prefix-prefix i a f q = Prefix n q- where- n = NameAtom (InterchangeName i UCUM True) a f--ucumMetric :: String -> String -> String -> UnitName 'Metric-ucumMetric i a f = MetricAtomic $ NameAtom (InterchangeName i UCUM True) a f--ucum :: String -> String -> String -> UnitName 'NonMetric-ucum i a f = Atomic $ NameAtom (InterchangeName i UCUM True) a f--dimensionalAtom :: String -> String -> String -> UnitName 'NonMetric-dimensionalAtom i a f = Atomic $ NameAtom (InterchangeName i DimensionalLibrary True) a f---- | Constructs an atomic name for a custom unit.-atom :: String -- ^ Interchange name- -> String -- ^ Abbreviated name in international English- -> String -- ^ Full name in international English- -> UnitName 'NonMetric-atom i a f = Atomic $ NameAtom (InterchangeName i Custom True) a f---- | The type of a unit name transformation that may be associated with an operation that takes a single unit as input.-type UnitNameTransformer = (forall m.UnitName m -> UnitName 'NonMetric)---- | The type of a unit name transformation that may be associated with an operation that takes two units as input.-type UnitNameTransformer2 = (forall m1 m2.UnitName m1 -> UnitName m2 -> UnitName 'NonMetric)---- | Forms the product of a list of 'UnitName's.------ If you wish to form a heterogenous product of 'Metric' and 'NonMetric' units--- you should apply 'weaken' to the 'Metric' ones.-product :: Foldable f => f (UnitName 'NonMetric) -> UnitName 'NonMetric-product = go . toList- where- -- This is not defined using a simple fold so that it does not complicate the product with- -- valid but meaningless occurences of nOne.- go :: [UnitName 'NonMetric] -> UnitName 'NonMetric- go [] = nOne- go [n] = n- go (n : ns) = n * go ns+{-# OPTIONS_HADDOCK not-home #-} + +{-# LANGUAGE AutoDeriveTypeable #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE NumDecimals #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} + +module Numeric.Units.Dimensional.UnitNames.Internal +where + +import Control.DeepSeq +import Control.Monad (join) +import Data.Coerce +import Data.Data hiding (Prefix) +#if MIN_VERSION_base(4, 8, 0) +import Data.Foldable (toList) +#else +import Data.Foldable (Foldable, toList) +#endif +import Data.Ord +import GHC.Generics hiding (Prefix) +import Numeric.Units.Dimensional.Dimensions.TermLevel (Dimension', asList, HasDimension(..)) +import Numeric.Units.Dimensional.UnitNames.InterchangeNames hiding (isAtomic) +import qualified Numeric.Units.Dimensional.UnitNames.InterchangeNames as I +import Numeric.Units.Dimensional.Variants (Metricality(..)) +import Prelude hiding ((*), (/), (^), product) +import qualified Prelude as P + +-- | The name of a unit. +data UnitName (m :: Metricality) where + -- The name of the unit of dimensionless values. + One :: UnitName 'NonMetric + -- A name of an atomic unit to which metric prefixes may be applied. + MetricAtomic :: NameAtom ('UnitAtom 'Metric) -> UnitName 'Metric + -- A name of an atomic unit to which metric prefixes may not be applied. + Atomic :: NameAtom ('UnitAtom 'NonMetric) -> UnitName 'NonMetric + -- A name of a prefixed unit. + Prefixed :: PrefixName -> UnitName 'Metric -> UnitName 'NonMetric + -- A compound name formed from the product of two names. + Product :: UnitName 'NonMetric -> UnitName 'NonMetric -> UnitName 'NonMetric + -- A compound name formed from the quotient of two names. + Quotient :: UnitName 'NonMetric -> UnitName 'NonMetric -> UnitName 'NonMetric + -- A compound name formed by raising a unit name to an integer power. + Power :: UnitName 'NonMetric -> Int -> UnitName 'NonMetric + -- A compound name formed by grouping another name, which is generally compound. + Grouped :: UnitName 'NonMetric -> UnitName 'NonMetric + -- A weakened name formed by forgetting that it could accept a metric prefix. + -- + -- Also available is the smart constructor `weaken` which accepts any `UnitName` as input. + Weaken :: UnitName 'Metric -> UnitName 'NonMetric + deriving (Typeable) + +deriving instance Eq (UnitName m) + +-- As it is for a GADT, this instance cannot be derived or use the generic default implementation +instance NFData (UnitName m) where + rnf n = case n of + One -> () + MetricAtomic a -> rnf a + Atomic a -> rnf a + Prefixed p n' -> rnf p `seq` rnf n' + Product n1 n2 -> rnf n1 `seq` rnf n2 + Quotient n1 n2 -> rnf n1 `seq` rnf n2 + Power n' e -> rnf n' `seq` rnf e + Grouped n' -> rnf n' + Weaken n' -> rnf n' + +instance Show (UnitName m) where + show One = "1" + show (MetricAtomic a) = abbreviation_en a + show (Atomic a) = abbreviation_en a + show (Prefixed a n) = abbreviation_en a ++ show n + show (Product n1 n2) = show n1 ++ " " ++ show n2 + show (Quotient n1 n2) = show n1 ++ " / " ++ show n2 + show (Power x n) = show x ++ "^" ++ show n + show (Grouped n) = "(" ++ show n ++ ")" + show (Weaken n) = show n + +asAtomic :: UnitName m -> Maybe (NameAtom ('UnitAtom m)) +asAtomic (MetricAtomic a) = Just a +asAtomic (Atomic a) = Just a +asAtomic (Weaken n) = fmap coerce $ asAtomic n +asAtomic _ = Nothing + +isAtomic :: UnitName m -> Bool +isAtomic (One) = True +isAtomic (MetricAtomic _) = True +isAtomic (Atomic _) = True +isAtomic (Prefixed _ _) = True +isAtomic (Grouped _) = True +isAtomic (Weaken n) = isAtomic n +isAtomic _ = False + +isAtomicOrProduct :: UnitName m -> Bool +isAtomicOrProduct (Product _ _) = True +isAtomicOrProduct n = isAtomic n + +-- reduce by algebraic simplifications +reduce :: UnitName m -> UnitName m +reduce (One) = One +reduce n@(MetricAtomic _) = n +reduce n@(Atomic _) = n +reduce n@(Prefixed _ _) = n +reduce (Product n1 n2) = reduce' (reduce n1 * reduce n2) +reduce (Quotient n1 n2) = reduce' (reduce n1 * reduce n2) +reduce (Power n x) = reduce' ((reduce n) ^ x) +reduce (Grouped n) = reduce' (Grouped (reduce n)) +reduce (Weaken n) = reduce' (Weaken (reduce n)) + +-- reduce, knowing that subterms are already in reduced form +reduce' :: UnitName m -> UnitName m +reduce' (Product One n) = reduce' n +reduce' (Product n One) = reduce' n +reduce' (Power (Power n x1) x2) = reduce (n ^ (x1 P.* x2)) +reduce' (Power (Grouped (Power n x1)) x2) = reduce (n ^ (x1 P.* x2)) +reduce' (Power _ 0) = One +reduce' (Power n 1) = reduce' n +reduce' (Grouped n) = reduce' n +reduce' n@(Weaken (MetricAtomic _)) = n +reduce' n = n + +data NameAtomType = UnitAtom Metricality + | PrefixAtom + deriving (Eq, Ord, Data, Typeable, Generic) + +instance NFData NameAtomType where -- instance is derived from Generic instance + +-- | The name of a metric prefix. +type PrefixName = NameAtom 'PrefixAtom + +data Prefix = Prefix + { + -- | The name of a metric prefix. + prefixName :: PrefixName, + -- | The scale factor denoted by a metric prefix. + scaleFactor :: Rational + } + deriving (Eq, Data, Typeable, Generic) + +instance Ord Prefix where + compare = comparing scaleFactor + +instance NFData Prefix where -- instance is derived from Generic instance + +instance HasInterchangeName Prefix where + interchangeName = interchangeName . prefixName + +-- | The name of the unit of dimensionless values. +nOne :: UnitName 'NonMetric +nOne = One + +nMeter :: UnitName 'Metric +nMeter = ucumMetric "m" "m" "metre" + +nGram :: UnitName 'Metric +nGram = ucumMetric "g" "g" "gram" + +nKilogram :: UnitName 'NonMetric +nKilogram = applyPrefix kilo nGram + +nSecond :: UnitName 'Metric +nSecond = ucumMetric "s" "s" "second" + +nAmpere :: UnitName 'Metric +nAmpere = ucumMetric "A" "A" "Ampere" + +nKelvin :: UnitName 'Metric +nKelvin = ucumMetric "K" "K" "Kelvin" + +nMole :: UnitName 'Metric +nMole = ucumMetric "mol" "mol" "mole" + +nCandela :: UnitName 'Metric +nCandela = ucumMetric "cd" "cd" "candela" + +-- | The name of the base unit associated with a specified dimension. +baseUnitName :: Dimension' -> UnitName 'NonMetric +baseUnitName d = let powers = asList $ dimension d + in reduce . product $ zipWith (^) baseUnitNames powers + +baseUnitNames :: [UnitName 'NonMetric] +baseUnitNames = [weaken nMeter, nKilogram, weaken nSecond, weaken nAmpere, weaken nKelvin, weaken nMole, weaken nCandela] + +deka, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta :: Prefix +deka = prefix "da" "da" "deka" 1e1 +hecto = prefix "h" "h" "hecto" 1e2 +kilo = prefix "k" "k" "kilo" 1e3 +mega = prefix "M" "M" "mega" 1e6 +giga = prefix "G" "G" "giga" 1e9 +tera = prefix "T" "T" "tera" 1e12 +peta = prefix "P" "P" "peta" 1e15 +exa = prefix "E" "E" "exa" 1e18 +zetta = prefix "Z" "Z" "zetta" 1e21 +yotta = prefix "Y" "Y" "yotta" 1e24 +deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto :: Prefix +deci = prefix "d" "d" "deci" 1e-1 +centi = prefix "c" "c" "centi" 1e-2 +milli = prefix "m" "m" "milli" 1e-3 +micro = prefix "u" "μ" "micro" 1e-6 +nano = prefix "n" "n" "nano" 1e-9 +pico = prefix "p" "p" "pico" 1e-12 +femto = prefix "f" "f" "femto" 1e-15 +atto = prefix "a" "a" "atto" 1e-18 +zepto = prefix "z" "z" "zepto" 1e-21 +yocto = prefix "y" "y" "yocto" 1e-24 + +-- | A list of all 'Prefix'es defined by the SI. +siPrefixes :: [Prefix] +siPrefixes = [yocto, zepto, atto, femto, pico, nano, micro, milli, centi, deci, deka, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta] + +-- | Forms a 'UnitName' from a 'Metric' name by applying a metric prefix. +applyPrefix :: Prefix -> UnitName 'Metric -> UnitName 'NonMetric +applyPrefix = Prefixed . prefixName + +{- +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. +-} + +infixr 8 ^ +infixl 7 *, / + +-- | Form a 'UnitName' by taking the product of two others. +(*) :: UnitName m1 -> UnitName m2 -> UnitName 'NonMetric +a * b = Product (weaken a) (weaken b) + +-- | Form a 'UnitName' by dividing one by another. +(/) :: UnitName m1 -> UnitName m2 -> UnitName 'NonMetric +n1 / n2 | isAtomicOrProduct n1 = Quotient (weaken n1) (weaken n2) + | otherwise = Quotient (grouped n1) (weaken n2) + +-- | Form a 'UnitName' by raising a name to an integer power. +(^) :: UnitName m -> Int -> UnitName 'NonMetric +x ^ n | isAtomic x = Power (weaken x) n + | otherwise = Power (grouped x) n + +-- | Convert a 'UnitName' which may or may not be 'Metric' to one +-- which is certainly 'NonMetric'. +weaken :: UnitName m -> UnitName 'NonMetric +weaken n@(MetricAtomic _) = Weaken n -- we really only need this one case and a catchall, but the typechecker can't see it +weaken n@One = n +weaken n@(Atomic _) = n +weaken n@(Prefixed _ _) = n +weaken n@(Product _ _) = n +weaken n@(Quotient _ _) = n +weaken n@(Power _ _) = n +weaken n@(Grouped _) = n +weaken n@(Weaken _) = n + +-- | Attempt to convert a 'UnitName' which may or may not be 'Metric' to one +-- which is certainly 'Metric'. +strengthen :: UnitName m -> Maybe (UnitName 'Metric) +strengthen n@(MetricAtomic _) = Just n +strengthen (Weaken n) = strengthen n +strengthen _ = Nothing + +-- | Convert a 'UnitName' of one 'Metricality' into a name of another metricality by +-- strengthening or weakening if neccessary. Because it may not be possible to strengthen, +-- the result is returned in a 'Maybe' wrapper. +relax :: forall m1 m2.(Typeable m1, Typeable m2) => UnitName m1 -> Maybe (UnitName m2) +relax n = go (typeRep (Proxy :: Proxy m1)) (typeRep (Proxy :: Proxy m2)) n + where + metric = typeRep (Proxy :: Proxy 'Metric) + nonMetric = typeRep (Proxy :: Proxy 'NonMetric) + go :: TypeRep -> TypeRep -> UnitName m1 -> Maybe (UnitName m2) + go p1 p2 | p1 == p2 = cast + | (p1 == nonMetric) && (p2 == metric) = join . fmap gcast . strengthen + | (p1 == metric) && (p2 == nonMetric) = cast . weaken + | otherwise = error "Should be unreachable. TypeRep of an unexpected Metricality encountered." + +-- | Constructs a 'UnitName' by applying a grouping operation to +-- another 'UnitName', which may be useful to express precedence. +grouped :: UnitName m -> UnitName 'NonMetric +grouped = Grouped . weaken + +-- | Represents the name of an atomic unit or prefix. +data NameAtom (m :: NameAtomType) + = NameAtom + { + _interchangeName :: InterchangeName, -- ^ The interchange name of the unit. + abbreviation_en :: String, -- ^ The abbreviated name of the unit in international English + name_en :: String -- ^ The full name of the unit in international English + } + deriving (Eq, Ord, Data, Typeable, Generic) + +instance NFData (NameAtom m) where -- instance is derived from Generic instance + +instance HasInterchangeName (NameAtom m) where + interchangeName = _interchangeName + +instance HasInterchangeName (UnitName m) where + interchangeName One = InterchangeName { name = "1", authority = UCUM, I.isAtomic = True } + interchangeName (MetricAtomic a) = interchangeName a + interchangeName (Atomic a) = interchangeName a + interchangeName (Prefixed p n) = let n' = (name . interchangeName $ p) ++ (name . interchangeName $ n) + a' = max (authority . interchangeName $ p) (authority . interchangeName $ n) + in InterchangeName { name = n', authority = a', I.isAtomic = False } + interchangeName (Product n1 n2) = let n' = (name . interchangeName $ n1) ++ "." ++ (name . interchangeName $ n2) + a' = max (authority . interchangeName $ n1) (authority . interchangeName $ n2) + in InterchangeName { name = n', authority = a', I.isAtomic = False } + interchangeName (Quotient n1 n2) = let n' = (name . interchangeName $ n1) ++ "/" ++ (name . interchangeName $ n2) + a' = max (authority . interchangeName $ n1) (authority . interchangeName $ n2) + in InterchangeName { name = n', authority = a', I.isAtomic = False } + -- TODO #109: note in this case that the UCUM is changing their grammar to not accept exponents after + -- as a result it will become necessary to distribute the exponentiation over the items in the base name + -- prior to generating the interchange name + interchangeName (Power n x) = let n' = (name . interchangeName $ n) ++ (show x) + in InterchangeName { name = n', authority = authority . interchangeName $ n, I.isAtomic = False } + interchangeName (Grouped n) = let n' = "(" ++ (name . interchangeName $ n) ++ ")" + in InterchangeName { name = n', authority = authority . interchangeName $ n, I.isAtomic = False } + interchangeName (Weaken n) = interchangeName n + +prefix :: String -> String -> String -> Rational -> Prefix +prefix i a f q = Prefix n q + where + n = NameAtom (InterchangeName i UCUM True) a f + +ucumMetric :: String -> String -> String -> UnitName 'Metric +ucumMetric i a f = MetricAtomic $ NameAtom (InterchangeName i UCUM True) a f + +ucum :: String -> String -> String -> UnitName 'NonMetric +ucum i a f = Atomic $ NameAtom (InterchangeName i UCUM True) a f + +dimensionalAtom :: String -> String -> String -> UnitName 'NonMetric +dimensionalAtom i a f = Atomic $ NameAtom (InterchangeName i DimensionalLibrary True) a f + +-- | Constructs an atomic name for a custom unit. +atom :: String -- ^ Interchange name + -> String -- ^ Abbreviated name in international English + -> String -- ^ Full name in international English + -> UnitName 'NonMetric +atom i a f = Atomic $ NameAtom (InterchangeName i Custom True) a f + +-- | The type of a unit name transformation that may be associated with an operation that takes a single unit as input. +type UnitNameTransformer = (forall m.UnitName m -> UnitName 'NonMetric) + +-- | The type of a unit name transformation that may be associated with an operation that takes two units as input. +type UnitNameTransformer2 = (forall m1 m2.UnitName m1 -> UnitName m2 -> UnitName 'NonMetric) + +-- | Forms the product of a list of 'UnitName's. +-- +-- If you wish to form a heterogenous product of 'Metric' and 'NonMetric' units +-- you should apply 'weaken' to the 'Metric' ones. +product :: Foldable f => f (UnitName 'NonMetric) -> UnitName 'NonMetric +product = go . toList + where + -- This is not defined using a simple fold so that it does not complicate the product with + -- valid but meaningless occurences of nOne. + go :: [UnitName 'NonMetric] -> UnitName 'NonMetric + go [] = nOne + go [n] = n + go (n : ns) = n * go ns
src/Numeric/Units/Dimensional/Variants.hs view
@@ -1,94 +1,94 @@-{-# OPTIONS_HADDOCK not-home, show-extensions #-}--{-# LANGUAGE AutoDeriveTypeable #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--{- |- Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter- License : BSD3-- Maintainer : bjorn@buckwalter.se- Stability : Stable- Portability: GHC only--Provides a type level representation of 'Variant's of dimensional values,-which may be quantities or units.--}-module Numeric.Units.Dimensional.Variants-(- type Variant(..),- Metricality(..),- type (*), type (/), type Weaken,- type CompatibleVariants-)-where--import Control.DeepSeq-import Data.Data-import qualified Data.ExactPi.TypeLevel as E-import GHC.Generics-import Prelude---- | Encodes whether a unit is a metric unit, that is, whether it can be combined--- with a metric prefix to form a related unit.-data Metricality = Metric -- ^ Capable of receiving a metric prefix.- | NonMetric -- ^ Incapable of receiving a metric prefix.- deriving (Eq, Ord, Data, Typeable, Generic)--instance NFData Metricality where -- instance is derived from Generic instance--{--The variety 'v' of 'Dimensional'--The phantom type variable v is used to distinguish between units-and quantities. It must be one of the following:--}---- | The kind of variants of dimensional values.-data Variant = DQuantity E.ExactPi' -- ^ The value is a quantity, stored as an `ExactPi` multiple of its value in its dimension's SI coherent unit.- | DUnit Metricality -- ^ The value is a unit, possibly a 'Metric' unit.- deriving (Typeable, Generic)--{--We will reuse the operators and function names from the Prelude.-To prevent unpleasant surprises we give operators the same fixity-as the Prelude.--}--infixl 7 *---- | Forms the product of two 'Variant's.------ The product of units is a non-metric unit.------ The product of quantities is a quantity.-type family (v1 :: Variant) * (v2 :: Variant) :: Variant where- 'DUnit m1 * 'DUnit m2 = 'DUnit 'NonMetric- 'DQuantity s1 * 'DQuantity s2 = 'DQuantity (s1 E.* s2)--type family (v1 :: Variant) / (v2 :: Variant) :: Variant where- 'DUnit m1 / 'DUnit m2 = 'DUnit 'NonMetric- 'DQuantity s1 / 'DQuantity s2 = 'DQuantity (s1 E./ s2)---- | Weakens a 'Variant' by forgetting possibly uninteresting type-level information.-type family Weaken (v :: Variant) :: Variant where- Weaken ('DQuantity s) = 'DQuantity s- Weaken ('DUnit m) = 'DUnit 'NonMetric---- | Two 'Variant's are compatible when dimensional values of the first may be converted--- into the second merely by changing the representation of their values.-type family AreCompatible (v1 :: Variant) (v2 :: Variant) :: Bool where- AreCompatible ('DQuantity s1) ('DQuantity s2) = 'True- AreCompatible ('DUnit m) ('DUnit 'NonMetric) = 'True- AreCompatible s s = 'True- AreCompatible s1 s2 = 'False---- | Two 'Variant's are compatible when dimensional values of the first may be converted--- into the second merely by changing the representation of their values.-type CompatibleVariants v1 v2 = ('True ~ AreCompatible v1 v2)+{-# OPTIONS_HADDOCK not-home, show-extensions #-} + +{-# LANGUAGE AutoDeriveTypeable #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + +{- | + Copyright : Copyright (C) 2006-2018 Bjorn Buckwalter + License : BSD3 + + Maintainer : bjorn@buckwalter.se + Stability : Stable + Portability: GHC only + +Provides a type level representation of 'Variant's of dimensional values, +which may be quantities or units. +-} +module Numeric.Units.Dimensional.Variants +( + type Variant(..), + Metricality(..), + type (*), type (/), type Weaken, + type CompatibleVariants +) +where + +import Control.DeepSeq +import Data.Data +import qualified Data.ExactPi.TypeLevel as E +import GHC.Generics +import Prelude + +-- | Encodes whether a unit is a metric unit, that is, whether it can be combined +-- with a metric prefix to form a related unit. +data Metricality = Metric -- ^ Capable of receiving a metric prefix. + | NonMetric -- ^ Incapable of receiving a metric prefix. + deriving (Eq, Ord, Data, Typeable, Generic) + +instance NFData Metricality where -- instance is derived from Generic instance + +{- +The variety 'v' of 'Dimensional' + +The phantom type variable v is used to distinguish between units +and quantities. It must be one of the following: +-} + +-- | The kind of variants of dimensional values. +data Variant = DQuantity E.ExactPi' -- ^ The value is a quantity, stored as an `ExactPi` multiple of its value in its dimension's SI coherent unit. + | DUnit Metricality -- ^ The value is a unit, possibly a 'Metric' unit. + deriving (Typeable, Generic) + +{- +We will reuse the operators and function names from the Prelude. +To prevent unpleasant surprises we give operators the same fixity +as the Prelude. +-} + +infixl 7 * + +-- | Forms the product of two 'Variant's. +-- +-- The product of units is a non-metric unit. +-- +-- The product of quantities is a quantity. +type family (v1 :: Variant) * (v2 :: Variant) :: Variant where + 'DUnit m1 * 'DUnit m2 = 'DUnit 'NonMetric + 'DQuantity s1 * 'DQuantity s2 = 'DQuantity (s1 E.* s2) + +type family (v1 :: Variant) / (v2 :: Variant) :: Variant where + 'DUnit m1 / 'DUnit m2 = 'DUnit 'NonMetric + 'DQuantity s1 / 'DQuantity s2 = 'DQuantity (s1 E./ s2) + +-- | Weakens a 'Variant' by forgetting possibly uninteresting type-level information. +type family Weaken (v :: Variant) :: Variant where + Weaken ('DQuantity s) = 'DQuantity s + Weaken ('DUnit m) = 'DUnit 'NonMetric + +-- | Two 'Variant's are compatible when dimensional values of the first may be converted +-- into the second merely by changing the representation of their values. +type family AreCompatible (v1 :: Variant) (v2 :: Variant) :: Bool where + AreCompatible ('DQuantity s1) ('DQuantity s2) = 'True + AreCompatible ('DUnit m) ('DUnit 'NonMetric) = 'True + AreCompatible s s = 'True + AreCompatible s1 s2 = 'False + +-- | Two 'Variant's are compatible when dimensional values of the first may be converted +-- into the second merely by changing the representation of their values. +type CompatibleVariants v1 v2 = ('True ~ AreCompatible v1 v2)
tests/DocTests.hs view
@@ -1,10 +1,17 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeSynonymInstances #-}--module Main (main) where--import System.FilePath.Glob (glob)-import Test.DocTest (doctest)--main :: IO ()-main = glob "src/**/*.hs" >>= doctest+{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TypeSynonymInstances #-} + +module Main (main) where + +import System.FilePath.Glob (glob) +import Test.DocTest (doctest) + +#if MIN_VERSION_base(4,12,0) +doctestFlags = ["-XNoStarIsType"] +#else +doctestFlags = [] +#endif + +main :: IO () +main = glob "src/**/*.hs" >>= (doctest . (doctestFlags++))
tests/Numeric/Units/Dimensional/DynamicSpec.hs view
@@ -1,157 +1,157 @@-module Numeric.Units.Dimensional.DynamicSpec where--import Numeric.Units.Dimensional.Prelude-import Numeric.Units.Dimensional.Dynamic hiding ((*),(/),(^),(*~),(/~), recip)-import Numeric.Units.Dimensional.Dimensions.TermLevel (hasSomeDimension)-import qualified Numeric.Units.Dimensional.Dynamic as Dyn-import qualified Prelude as P-import Test.Hspec-import Test.QuickCheck--spec :: Spec-spec = do- describe "Dynamic quantity promotion and demotion" $ do- it "round-trips through AnyQuantity" $ property $- \x -> let x' = x *~ kilo newton- x'' = demoteQuantity x' :: AnyQuantity Double- in Just x' == promoteQuantity x''- it "round-trips through DynQuantity" $ property $- \x -> let x' = x *~ micro watt- x'' = demoteQuantity x' :: DynQuantity Rational- in Just x' == promoteQuantity x''- it "round-trips through AnyQuantity then DynQuantity" $ property $- \x -> let x' = x *~ gram- x'' = demoteQuantity x' :: AnyQuantity Double- x''' = demoteQuantity x'' :: DynQuantity Double- in Just x' == promoteQuantity x'''- it "doesn't promote invalid quantities" $ do- (promoteQuantity invalidQuantity :: Maybe (Length Double)) `shouldBe` Nothing- it "doesn't promote AnyQuantity to the wrong dimension" $ do- let x = 12.3 *~ meter- x' = demoteQuantity x :: AnyQuantity Double- (promoteQuantity x' :: Maybe (Mass Double)) `shouldBe` Nothing- it "doesn't promote DynQuantity to the wrong dimension" $ do- let x = 12.3 *~ mole- x' = demoteQuantity x :: DynQuantity Double- (promoteQuantity x' :: Maybe (Time Double)) `shouldBe` Nothing- it "properly combines with dynamic units" $ do- let meter' = demoteUnit' meter- (promoteQuantity (139.4 Dyn.*~ meter' :: AnyQuantity Double)) `shouldBe` Just (139.4 *~ meter)- it "properly eliminates dynamic units" $ do- let ampere' = demoteUnit' ampere- i = demoteQuantity $ 47 *~ ampere :: AnyQuantity Double- i Dyn./~ ampere' `shouldBe` Just 47- it "doesn't eliminate dynamic units of the wrong dimension" $ do- let ampere' = demoteUnit' ampere- i = demoteQuantity $ 47 *~ joule :: AnyQuantity Double- i Dyn./~ ampere' `shouldBe` Nothing- describe "DynQuantity arithmetic" $ do- -- declare some static quantities and their dynamic counterparts for arithmetic tests- let x1 = 12.3 *~ meter- x2 = (-7.9) *~ meter- a = 93 *~ square (kilo meter)- m = 147 *~ kilo gram- t = 14.9 *~ second- f = 87.2 *~ milli newton- phi = 1.61803398875 *~ one- x1' = demoteQuantity x1 :: DynQuantity Double- x2' = demoteQuantity x2 :: DynQuantity Double- a' = demoteQuantity a :: DynQuantity Double- m' = demoteQuantity m :: DynQuantity Double- t' = demoteQuantity t :: DynQuantity Double- f' = demoteQuantity f :: DynQuantity Double- phi' = demoteQuantity phi :: DynQuantity Double- context "Num instance" $ do- it "matches static addition" $ do- (x1' P.+ x2') `shouldBe` demoteQuantity (x1 + x2)- it "allows addition with polydimensional zero" $ do- (t' P.+ polydimensionalZero) `shouldBe` t'- (polydimensionalZero P.+ t') `shouldBe` t'- (polydimensionalZero P.+ polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) - it "propagates witnesses to zero during addition" $ do- -- We want to test that the witness for polymorphic zero was actually added to the other addend.- -- The reason for this property is that if the other addend is some element of the underlying type- -- which can't act as a divisor (such as a propagating nAn), then we want that information to still- -- be around when we go to promote the result.- let nan = 0 P./ 0 :: Double- x = demoteQuantity $ nan *~ meter- Just y = promoteQuantity (polydimensionalZero P.+ x) :: Maybe (Length Double)- (y /~ meter) `shouldSatisfy` P.isNaN- it "matches static subtraction" $ do- (x2' P.- x1') `shouldBe` demoteQuantity (x2 - x1)- it "allows subtraction with polydimensional zero" $ do- (m' P.- polydimensionalZero) `shouldBe` m'- (polydimensionalZero P.- m') `shouldBe` (P.negate m')- (polydimensionalZero P.- polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) - it "matches static multiplication" $ do- promoteQuantity (x1' P.* f') `shouldBe` Just (x1 * f)- it "allows multiplication with polydimensional zero" $ do- (f' P.* polydimensionalZero) `shouldBe` polydimensionalZero- (polydimensionalZero P.* m') `shouldBe` polydimensionalZero- (polydimensionalZero P.* polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)- it "matches static negation" $ do- (P.negate m') `shouldBe` demoteQuantity (negate m)- it "negates polydimensional zero" $ do- (P.negate polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)- it "matches static absolute value" $ do- (P.abs x2') `shouldBe` demoteQuantity (abs x2)- it "takes absolute value of polydimensional zero" $ do- (P.abs polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)- it "matches static signum" $ do- (P.signum x1') `shouldBe` demoteQuantity (signum x1)- (P.signum x2') `shouldBe` demoteQuantity (signum x2)- it "takes signum of polydimensional zero" $ do- (P.signum polydimensionalZero) `shouldBe` demoteQuantity (_0 :: Dimensionless Double)- it "implements fromInteger with dimensionless result" $ do- (P.fromInteger 7 :: DynQuantity Double) `shouldBe` demoteQuantity _7- context "Fractional instance" $ do- it "matches static division" $ do- ((f' P.* x1') P./ t') `shouldBe` demoteQuantity ((f * x1) / t)- it "matches static reciprocal" $ do- (P.recip m') `shouldBe` demoteQuantity (recip m)- it "implements fromRational with dimensionless result" $ do- let pi' = 22 P./ 7 :: Rational- (P.fromRational pi' :: DynQuantity Rational) `shouldBe` demoteQuantity (pi' *~ one)- it "permits polydimensional zero as a dividend" $ do- (polydimensionalZero P./ m') `shouldBe` polydimensionalZero- it "propagates witnesses to zero during division" $ do- -- We want to test that the witness for polymorphic zero was actually divided by the divisor.- -- The reason for this property is that if the divisor is itself zero (but not polydimensionalZero),- -- or some other element of the underlying type which can't act as a divisor (such as a propagating nAn),- -- then we want that information to still be around when we go to promote the result.- let nan = 0 P./ 0 :: Double- x = demoteQuantity $ nan *~ meter- y = polydimensionalZero P./ x- Just y' = promoteQuantity y :: Maybe (Length Double)- (y' /~ meter) `shouldSatisfy` P.isNaN- context "Floating instance" $ do- it "implements dimensionless pi" $ do- (P.pi :: DynQuantity Double) `shouldBe` demoteQuantity pi- it "implements dimensionless sin" $ do- -- this will serve as a test for all the single-argument dimensionless functions- (P.sin phi') `shouldBe` demoteQuantity (sin phi)- it "rejects non-dimensionless arguments to sin" $ do- (P.sin m') `shouldBe` invalidQuantity- it "implements dimensionless sin of polydimensional zero" $ do- (P.sin polydimensionalZero) `shouldBe` (0 :: DynQuantity Double)- it "matches static square root" $ do- (P.sqrt a') `shouldBe` demoteQuantity (sqrt a)- it "rejects arguments to square root with non-square dimensions" $ do- (P.sqrt f') `shouldNotSatisfy` hasSomeDimension- it "takes the square root of polydimensional zero" $ do- (P.sqrt polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)- it "matches static dimensionless exponentiation" $ do- (phi' P.** phi') `shouldBe` demoteQuantity (phi ** phi)- it "rejects non-dimensionless arguments to dimensionless exponentiation" $ do- (phi' P.** m') `shouldNotSatisfy` hasSomeDimension- (x1' P.** phi') `shouldNotSatisfy` hasSomeDimension- it "matches static logBase" $ do- (P.logBase 10 phi') `shouldBe` demoteQuantity (logBase (10 *~ one) phi)- it "rejects non-dimensionless arguments to logBase" $ do- (P.logBase 10 x1') `shouldNotSatisfy` hasSomeDimension- (P.logBase x1' 10) `shouldNotSatisfy` hasSomeDimension- describe "Dynamic units" $ do- describe "Promotion and demotion" $ do- return ()- describe "Arithmetic" $ do- return ()+module Numeric.Units.Dimensional.DynamicSpec where + +import Numeric.Units.Dimensional.Prelude +import Numeric.Units.Dimensional.Dynamic hiding ((*),(/),(^),(*~),(/~), recip) +import Numeric.Units.Dimensional.Dimensions.TermLevel (hasSomeDimension) +import qualified Numeric.Units.Dimensional.Dynamic as Dyn +import qualified Prelude as P +import Test.Hspec +import Test.QuickCheck + +spec :: Spec +spec = do + describe "Dynamic quantity promotion and demotion" $ do + it "round-trips through AnyQuantity" $ property $ + \x -> let x' = x *~ kilo newton + x'' = demoteQuantity x' :: AnyQuantity Double + in Just x' == promoteQuantity x'' + it "round-trips through DynQuantity" $ property $ + \x -> let x' = x *~ micro watt + x'' = demoteQuantity x' :: DynQuantity Rational + in Just x' == promoteQuantity x'' + it "round-trips through AnyQuantity then DynQuantity" $ property $ + \x -> let x' = x *~ gram + x'' = demoteQuantity x' :: AnyQuantity Double + x''' = demoteQuantity x'' :: DynQuantity Double + in Just x' == promoteQuantity x''' + it "doesn't promote invalid quantities" $ do + (promoteQuantity invalidQuantity :: Maybe (Length Double)) `shouldBe` Nothing + it "doesn't promote AnyQuantity to the wrong dimension" $ do + let x = 12.3 *~ meter + x' = demoteQuantity x :: AnyQuantity Double + (promoteQuantity x' :: Maybe (Mass Double)) `shouldBe` Nothing + it "doesn't promote DynQuantity to the wrong dimension" $ do + let x = 12.3 *~ mole + x' = demoteQuantity x :: DynQuantity Double + (promoteQuantity x' :: Maybe (Time Double)) `shouldBe` Nothing + it "properly combines with dynamic units" $ do + let meter' = demoteUnit' meter + (promoteQuantity (139.4 Dyn.*~ meter' :: AnyQuantity Double)) `shouldBe` Just (139.4 *~ meter) + it "properly eliminates dynamic units" $ do + let ampere' = demoteUnit' ampere + i = demoteQuantity $ 47 *~ ampere :: AnyQuantity Double + i Dyn./~ ampere' `shouldBe` Just 47 + it "doesn't eliminate dynamic units of the wrong dimension" $ do + let ampere' = demoteUnit' ampere + i = demoteQuantity $ 47 *~ joule :: AnyQuantity Double + i Dyn./~ ampere' `shouldBe` Nothing + describe "DynQuantity arithmetic" $ do + -- declare some static quantities and their dynamic counterparts for arithmetic tests + let x1 = 12.3 *~ meter + x2 = (-7.9) *~ meter + a = 93 *~ square (kilo meter) + m = 147 *~ kilo gram + t = 14.9 *~ second + f = 87.2 *~ milli newton + phi = 1.61803398875 *~ one + x1' = demoteQuantity x1 :: DynQuantity Double + x2' = demoteQuantity x2 :: DynQuantity Double + a' = demoteQuantity a :: DynQuantity Double + m' = demoteQuantity m :: DynQuantity Double + t' = demoteQuantity t :: DynQuantity Double + f' = demoteQuantity f :: DynQuantity Double + phi' = demoteQuantity phi :: DynQuantity Double + context "Num instance" $ do + it "matches static addition" $ do + (x1' P.+ x2') `shouldBe` demoteQuantity (x1 + x2) + it "allows addition with polydimensional zero" $ do + (t' P.+ polydimensionalZero) `shouldBe` t' + (polydimensionalZero P.+ t') `shouldBe` t' + (polydimensionalZero P.+ polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "propagates witnesses to zero during addition" $ do + -- We want to test that the witness for polymorphic zero was actually added to the other addend. + -- The reason for this property is that if the other addend is some element of the underlying type + -- which can't act as a divisor (such as a propagating nAn), then we want that information to still + -- be around when we go to promote the result. + let nan = 0 P./ 0 :: Double + x = demoteQuantity $ nan *~ meter + Just y = promoteQuantity (polydimensionalZero P.+ x) :: Maybe (Length Double) + (y /~ meter) `shouldSatisfy` P.isNaN + it "matches static subtraction" $ do + (x2' P.- x1') `shouldBe` demoteQuantity (x2 - x1) + it "allows subtraction with polydimensional zero" $ do + (m' P.- polydimensionalZero) `shouldBe` m' + (polydimensionalZero P.- m') `shouldBe` (P.negate m') + (polydimensionalZero P.- polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "matches static multiplication" $ do + promoteQuantity (x1' P.* f') `shouldBe` Just (x1 * f) + it "allows multiplication with polydimensional zero" $ do + (f' P.* polydimensionalZero) `shouldBe` polydimensionalZero + (polydimensionalZero P.* m') `shouldBe` polydimensionalZero + (polydimensionalZero P.* polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "matches static negation" $ do + (P.negate m') `shouldBe` demoteQuantity (negate m) + it "negates polydimensional zero" $ do + (P.negate polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "matches static absolute value" $ do + (P.abs x2') `shouldBe` demoteQuantity (abs x2) + it "takes absolute value of polydimensional zero" $ do + (P.abs polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "matches static signum" $ do + (P.signum x1') `shouldBe` demoteQuantity (signum x1) + (P.signum x2') `shouldBe` demoteQuantity (signum x2) + it "takes signum of polydimensional zero" $ do + (P.signum polydimensionalZero) `shouldBe` demoteQuantity (_0 :: Dimensionless Double) + it "implements fromInteger with dimensionless result" $ do + (P.fromInteger 7 :: DynQuantity Double) `shouldBe` demoteQuantity _7 + context "Fractional instance" $ do + it "matches static division" $ do + ((f' P.* x1') P./ t') `shouldBe` demoteQuantity ((f * x1) / t) + it "matches static reciprocal" $ do + (P.recip m') `shouldBe` demoteQuantity (recip m) + it "implements fromRational with dimensionless result" $ do + let pi' = 22 P./ 7 :: Rational + (P.fromRational pi' :: DynQuantity Rational) `shouldBe` demoteQuantity (pi' *~ one) + it "permits polydimensional zero as a dividend" $ do + (polydimensionalZero P./ m') `shouldBe` polydimensionalZero + it "propagates witnesses to zero during division" $ do + -- We want to test that the witness for polymorphic zero was actually divided by the divisor. + -- The reason for this property is that if the divisor is itself zero (but not polydimensionalZero), + -- or some other element of the underlying type which can't act as a divisor (such as a propagating nAn), + -- then we want that information to still be around when we go to promote the result. + let nan = 0 P./ 0 :: Double + x = demoteQuantity $ nan *~ meter + y = polydimensionalZero P./ x + Just y' = promoteQuantity y :: Maybe (Length Double) + (y' /~ meter) `shouldSatisfy` P.isNaN + context "Floating instance" $ do + it "implements dimensionless pi" $ do + (P.pi :: DynQuantity Double) `shouldBe` demoteQuantity pi + it "implements dimensionless sin" $ do + -- this will serve as a test for all the single-argument dimensionless functions + (P.sin phi') `shouldBe` demoteQuantity (sin phi) + it "rejects non-dimensionless arguments to sin" $ do + (P.sin m') `shouldBe` invalidQuantity + it "implements dimensionless sin of polydimensional zero" $ do + (P.sin polydimensionalZero) `shouldBe` (0 :: DynQuantity Double) + it "matches static square root" $ do + (P.sqrt a') `shouldBe` demoteQuantity (sqrt a) + it "rejects arguments to square root with non-square dimensions" $ do + (P.sqrt f') `shouldNotSatisfy` hasSomeDimension + it "takes the square root of polydimensional zero" $ do + (P.sqrt polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double) + it "matches static dimensionless exponentiation" $ do + (phi' P.** phi') `shouldBe` demoteQuantity (phi ** phi) + it "rejects non-dimensionless arguments to dimensionless exponentiation" $ do + (phi' P.** m') `shouldNotSatisfy` hasSomeDimension + (x1' P.** phi') `shouldNotSatisfy` hasSomeDimension + it "matches static logBase" $ do + (P.logBase 10 phi') `shouldBe` demoteQuantity (logBase (10 *~ one) phi) + it "rejects non-dimensionless arguments to logBase" $ do + (P.logBase 10 x1') `shouldNotSatisfy` hasSomeDimension + (P.logBase x1' 10) `shouldNotSatisfy` hasSomeDimension + describe "Dynamic units" $ do + describe "Promotion and demotion" $ do + return () + describe "Arithmetic" $ do + return ()
tests/Numeric/Units/Dimensional/QuantitiesSpec.hs view
@@ -1,137 +1,137 @@-module Numeric.Units.Dimensional.QuantitiesSpec where--import Numeric.Units.Dimensional.Prelude-import Test.Hspec--spec :: Spec-spec = do- describe "Quantity synonyms" $ do- it "compile with correct dimensions" $ do- success -- If I compiled I'm OK!--success :: IO ()-success = return ()---- These definitions simply verify that the type synonyms are--- consistent with the appropriate units from table 2. If the--- definitions compile the type synonyms are good.--x1 :: Area Double-x1 = 1 *~ meter ^ pos2-x2 :: Volume Double-x2 = 1 *~ meter ^ pos3-x3 :: Velocity Double-x3 = 1 *~ (meter / second)-x4 :: Acceleration Double-x4 = 1 *~ (meter / second ^ pos2)-x5 :: WaveNumber Double-x5 = 1 *~ meter ^ neg1-x6 :: Density Double-x6 = 1 *~ (kilo gram / meter ^ pos3)-x7 :: SpecificVolume Double-x7 = 1 *~ (meter ^ pos3 / kilo gram)-x8 :: CurrentDensity Double-x8 = 1 *~ (ampere / meter ^ pos2)-x9 :: MagneticFieldStrength Double-x9 = 1 *~ (ampere / meter)-x10 :: Concentration Double-x10 = 1 *~ (mole / meter ^ pos3)-x11 :: Luminance Double-x11 = 1 *~ (candela / meter ^ pos2)---- These definitions simply verify that the type synonyms are--- consistent with the appropriate units from table 3. If the--- definitions compile the type synonyms are good.--y1 :: PlaneAngle Double-y1 = 1 *~ (meter / meter)-y2 :: SolidAngle Double-y2 = 1 *~ (meter ^ pos2 / meter ^ pos2)-y3 :: Frequency Double-y3 = 1 *~ (one / second)-y4 :: Force Double-y4 = 1 *~ (meter * kilo gram / second ^ pos2)-y5 :: Pressure Double-y5 = 1 *~ (newton / meter ^ pos2)-y6 :: Energy Double-y6 = 1 *~ (newton * meter)-y7 :: Power Double-y7 = 1 *~ (joule / second)-y8 :: ElectricCharge Double-y8 = 1 *~ (second * ampere)-y9 :: ElectricPotential Double-y9 = 1 *~ (watt / ampere)-y10 :: Capacitance Double-y10 = 1 *~ (coulomb / volt)-y11 :: ElectricResistance Double-y11 = 1 *~ (volt / ampere)-y12 :: ElectricConductance Double-y12 = 1 *~ (ampere / volt)-y13 :: MagneticFlux Double-y13 = 1 *~ (volt * second)-y14 :: MagneticFluxDensity Double-y14 = 1 *~ (weber / meter ^ pos2)-y15 :: Inductance Double-y15 = 1 *~ (weber / ampere)-y16 :: LuminousFlux Double-y16 = 1 *~ (candela * steradian)-y17 :: Illuminance Double-y17 = 1 *~ (lumen / meter ^ pos2)-y18 :: Activity Double-y18 = 1 *~ (one / second)-y19 :: AbsorbedDose Double-y19 = 1 *~ (joule / kilo gram)-y20 :: DoseEquivalent Double-y20 = 1 *~ (joule / kilo gram)-y21 :: CatalyticActivity Double-y21 = 1 *~ (mole / second)---- Verification of table 4. If the definitions compile the type--- synonyms are good.--z1 :: AngularVelocity Double-z1 = 1 *~ (radian / second)-z2 :: AngularAcceleration Double-z2 = 1 *~ (radian / second ^ pos2)-z3 :: DynamicViscosity Double-z3 = 1 *~ (pascal * second)-z4 :: MomentOfForce Double-z4 = 1 *~ (newton * meter)-z5 :: SurfaceTension Double-z5 = 1 *~ (newton / meter)-z6 :: HeatFluxDensity Double-z6 = 1 *~ (watt / meter ^ pos2)-z7 :: RadiantIntensity Double-z7 = 1 *~ (watt / steradian)-z8 :: Radiance Double-z8 = 1 *~ (watt / (meter ^ pos2 * steradian))-z9 :: HeatCapacity Double-z9 = 1 *~ (joule / kelvin)-z10 :: SpecificHeatCapacity Double-z10 = 1 *~ (joule / (kilo gram * kelvin))-z11 :: ThermalConductivity Double-z11 = 1 *~ (watt / (meter * kelvin))-z12 :: EnergyDensity Double-z12 = 1 *~ (joule / meter ^ pos3)-z13 :: ElectricFieldStrength Double-z13 = 1 *~ (volt / meter)-z14 :: ElectricChargeDensity Double-z14 = 1 *~ (coulomb / meter ^ pos3)-z15 :: ElectricFluxDensity Double-z15 = 1 *~ (coulomb / meter ^ pos2)-z16 :: Permittivity Double-z16 = 1 *~ (farad / meter)-z17 :: Permeability Double-z17 = 1 *~ (henry / meter)-z18 :: MolarEnergy Double-z18 = 1 *~ (joule / mole)-z19 :: MolarEntropy Double-z19 = 1 *~ (joule / (mole * kelvin))-z20 :: Exposure Double-z20 = 1 *~ (coulomb / kilo gram)-z21 :: AbsorbedDoseRate Double-z21 = 1 *~ (gray / second)---- Other quantitites.-mu :: GravitationalParameter Double-mu = 398600.4418 *~ (kilo meter ^ pos3 / second ^ pos2)+module Numeric.Units.Dimensional.QuantitiesSpec where + +import Numeric.Units.Dimensional.Prelude +import Test.Hspec + +spec :: Spec +spec = do + describe "Quantity synonyms" $ do + it "compile with correct dimensions" $ do + success -- If I compiled I'm OK! + +success :: IO () +success = return () + +-- These definitions simply verify that the type synonyms are +-- consistent with the appropriate units from table 2. If the +-- definitions compile the type synonyms are good. + +x1 :: Area Double +x1 = 1 *~ meter ^ pos2 +x2 :: Volume Double +x2 = 1 *~ meter ^ pos3 +x3 :: Velocity Double +x3 = 1 *~ (meter / second) +x4 :: Acceleration Double +x4 = 1 *~ (meter / second ^ pos2) +x5 :: WaveNumber Double +x5 = 1 *~ meter ^ neg1 +x6 :: Density Double +x6 = 1 *~ (kilo gram / meter ^ pos3) +x7 :: SpecificVolume Double +x7 = 1 *~ (meter ^ pos3 / kilo gram) +x8 :: CurrentDensity Double +x8 = 1 *~ (ampere / meter ^ pos2) +x9 :: MagneticFieldStrength Double +x9 = 1 *~ (ampere / meter) +x10 :: Concentration Double +x10 = 1 *~ (mole / meter ^ pos3) +x11 :: Luminance Double +x11 = 1 *~ (candela / meter ^ pos2) + +-- These definitions simply verify that the type synonyms are +-- consistent with the appropriate units from table 3. If the +-- definitions compile the type synonyms are good. + +y1 :: PlaneAngle Double +y1 = 1 *~ (meter / meter) +y2 :: SolidAngle Double +y2 = 1 *~ (meter ^ pos2 / meter ^ pos2) +y3 :: Frequency Double +y3 = 1 *~ (one / second) +y4 :: Force Double +y4 = 1 *~ (meter * kilo gram / second ^ pos2) +y5 :: Pressure Double +y5 = 1 *~ (newton / meter ^ pos2) +y6 :: Energy Double +y6 = 1 *~ (newton * meter) +y7 :: Power Double +y7 = 1 *~ (joule / second) +y8 :: ElectricCharge Double +y8 = 1 *~ (second * ampere) +y9 :: ElectricPotential Double +y9 = 1 *~ (watt / ampere) +y10 :: Capacitance Double +y10 = 1 *~ (coulomb / volt) +y11 :: ElectricResistance Double +y11 = 1 *~ (volt / ampere) +y12 :: ElectricConductance Double +y12 = 1 *~ (ampere / volt) +y13 :: MagneticFlux Double +y13 = 1 *~ (volt * second) +y14 :: MagneticFluxDensity Double +y14 = 1 *~ (weber / meter ^ pos2) +y15 :: Inductance Double +y15 = 1 *~ (weber / ampere) +y16 :: LuminousFlux Double +y16 = 1 *~ (candela * steradian) +y17 :: Illuminance Double +y17 = 1 *~ (lumen / meter ^ pos2) +y18 :: Activity Double +y18 = 1 *~ (one / second) +y19 :: AbsorbedDose Double +y19 = 1 *~ (joule / kilo gram) +y20 :: DoseEquivalent Double +y20 = 1 *~ (joule / kilo gram) +y21 :: CatalyticActivity Double +y21 = 1 *~ (mole / second) + +-- Verification of table 4. If the definitions compile the type +-- synonyms are good. + +z1 :: AngularVelocity Double +z1 = 1 *~ (radian / second) +z2 :: AngularAcceleration Double +z2 = 1 *~ (radian / second ^ pos2) +z3 :: DynamicViscosity Double +z3 = 1 *~ (pascal * second) +z4 :: MomentOfForce Double +z4 = 1 *~ (newton * meter) +z5 :: SurfaceTension Double +z5 = 1 *~ (newton / meter) +z6 :: HeatFluxDensity Double +z6 = 1 *~ (watt / meter ^ pos2) +z7 :: RadiantIntensity Double +z7 = 1 *~ (watt / steradian) +z8 :: Radiance Double +z8 = 1 *~ (watt / (meter ^ pos2 * steradian)) +z9 :: HeatCapacity Double +z9 = 1 *~ (joule / kelvin) +z10 :: SpecificHeatCapacity Double +z10 = 1 *~ (joule / (kilo gram * kelvin)) +z11 :: ThermalConductivity Double +z11 = 1 *~ (watt / (meter * kelvin)) +z12 :: EnergyDensity Double +z12 = 1 *~ (joule / meter ^ pos3) +z13 :: ElectricFieldStrength Double +z13 = 1 *~ (volt / meter) +z14 :: ElectricChargeDensity Double +z14 = 1 *~ (coulomb / meter ^ pos3) +z15 :: ElectricFluxDensity Double +z15 = 1 *~ (coulomb / meter ^ pos2) +z16 :: Permittivity Double +z16 = 1 *~ (farad / meter) +z17 :: Permeability Double +z17 = 1 *~ (henry / meter) +z18 :: MolarEnergy Double +z18 = 1 *~ (joule / mole) +z19 :: MolarEntropy Double +z19 = 1 *~ (joule / (mole * kelvin)) +z20 :: Exposure Double +z20 = 1 *~ (coulomb / kilo gram) +z21 :: AbsorbedDoseRate Double +z21 = 1 *~ (gray / second) + +-- Other quantitites. +mu :: GravitationalParameter Double +mu = 398600.4418 *~ (kilo meter ^ pos3 / second ^ pos2)
tests/Numeric/Units/DimensionalSpec.hs view
@@ -1,48 +1,48 @@-module Numeric.Units.DimensionalSpec where--import Numeric.Units.Dimensional.Prelude-import Test.Hspec-import qualified Prelude as P--spec :: Spec-spec = do- describe "Exponentiation operators" $ do- it "correctly exponentiate quantities with integer exponents" $ do- ((9::Double) *~ one) `shouldBe` (3 *~ one) ^ pos2- ((1::Double) *~ one) `shouldBe` (12.1231 *~ one) ^ zero- ((0.25::Double) *~ one) `shouldBe` (2 *~ one) ^ neg2- it "correctly exponentiate dimensionless quantities with floating point exponents" $ do- (3 P.** 2::Double) *~ one `shouldBe` (3 *~ one) ** (2 *~ one)- (3 P.** (-2.231)::Double) *~ one `shouldBe` (3 *~ one) ** ((-2.231) *~ one)-- describe "Show instance" $ do- it "properly prints basic quantities" $ do- show ((1.0::Double) *~ one) `shouldBe` "1.0"- show ((2.0::Double) *~ meter) `shouldBe` "2.0 m"- show ((2.0::Double) *~ (meter / second)) `shouldBe` "2.0 m s^-1"- show ((2.0::Double) *~ (meter ^ pos2 / second ^ pos2)) `shouldBe` "2.0 m^2 s^-2"-- describe "Ord instance" $ do- it "properly sorts quantities" $ do- compare ((1 :: Integer) *~ one) (3 *~ one) `shouldBe` LT- compare ((1 :: Double) *~ (kilo meter)) (1 *~ meter) `shouldBe` GT- compare ((0 :: Double) *~ second) (_0) `shouldBe` EQ-- describe "Enumeration function nFromTo" $ do- it "handles zero intermediate values" $ do- nFromTo' _1 _6 0 `shouldBe` [_1, _6]- it "handles negative number of intermediate values" $ do- nFromTo' _1 _6 (-1) `shouldBe` [_1, _6]- it "handles straightforward cases" $ do- nFromTo' _1 _3 1 `shouldBe` [_1, _2, _3]- nFromTo' _1 _6 4 `shouldBe` [_1, _2, _3, _4, _5, _6]- nFromTo' _0 _6 2 `shouldBe` [_0, _2, _4, _6]- it "handles decreasing intervals" $ do- nFromTo' _5 _2 2 `shouldBe` [_5, _4, _3, _2]- nFromTo' _6 _0 2 `shouldBe` [_6, _4, _2, _0]- it "handles empty intervals" $ do- nFromTo' _1 _1 0 `shouldBe` [_1, _1]- nFromTo' _0 _0 2 `shouldBe` [_0, _0, _0, _0]--nFromTo' :: Dimensionless Double -> Dimensionless Double -> Int -> [Dimensionless Double]-nFromTo' = nFromTo+module Numeric.Units.DimensionalSpec where + +import Numeric.Units.Dimensional.Prelude +import Test.Hspec +import qualified Prelude as P + +spec :: Spec +spec = do + describe "Exponentiation operators" $ do + it "correctly exponentiate quantities with integer exponents" $ do + ((9::Double) *~ one) `shouldBe` (3 *~ one) ^ pos2 + ((1::Double) *~ one) `shouldBe` (12.1231 *~ one) ^ zero + ((0.25::Double) *~ one) `shouldBe` (2 *~ one) ^ neg2 + it "correctly exponentiate dimensionless quantities with floating point exponents" $ do + (3 P.** 2::Double) *~ one `shouldBe` (3 *~ one) ** (2 *~ one) + (3 P.** (-2.231)::Double) *~ one `shouldBe` (3 *~ one) ** ((-2.231) *~ one) + + describe "Show instance" $ do + it "properly prints basic quantities" $ do + show ((1.0::Double) *~ one) `shouldBe` "1.0" + show ((2.0::Double) *~ meter) `shouldBe` "2.0 m" + show ((2.0::Double) *~ (meter / second)) `shouldBe` "2.0 m s^-1" + show ((2.0::Double) *~ (meter ^ pos2 / second ^ pos2)) `shouldBe` "2.0 m^2 s^-2" + + describe "Ord instance" $ do + it "properly sorts quantities" $ do + compare ((1 :: Integer) *~ one) (3 *~ one) `shouldBe` LT + compare ((1 :: Double) *~ (kilo meter)) (1 *~ meter) `shouldBe` GT + compare ((0 :: Double) *~ second) (_0) `shouldBe` EQ + + describe "Enumeration function nFromTo" $ do + it "handles zero intermediate values" $ do + nFromTo' _1 _6 0 `shouldBe` [_1, _6] + it "handles negative number of intermediate values" $ do + nFromTo' _1 _6 (-1) `shouldBe` [_1, _6] + it "handles straightforward cases" $ do + nFromTo' _1 _3 1 `shouldBe` [_1, _2, _3] + nFromTo' _1 _6 4 `shouldBe` [_1, _2, _3, _4, _5, _6] + nFromTo' _0 _6 2 `shouldBe` [_0, _2, _4, _6] + it "handles decreasing intervals" $ do + nFromTo' _5 _2 2 `shouldBe` [_5, _4, _3, _2] + nFromTo' _6 _0 2 `shouldBe` [_6, _4, _2, _0] + it "handles empty intervals" $ do + nFromTo' _1 _1 0 `shouldBe` [_1, _1] + nFromTo' _0 _0 2 `shouldBe` [_0, _0, _0, _0] + +nFromTo' :: Dimensionless Double -> Dimensionless Double -> Int -> [Dimensionless Double] +nFromTo' = nFromTo
tests/Spec.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}---- This module will automatically pull in all the Spec modules.--- See http://hspec.github.io/hspec-discover.html for a summary of how it works.+{-# OPTIONS_GHC -F -pgmF hspec-discover #-} + +-- This module will automatically pull in all the Spec modules. +-- See http://hspec.github.io/hspec-discover.html for a summary of how it works.