Copyright | Copyright (C) 2006-2018 Bjorn Buckwalter |
---|---|
License | BSD3 |
Maintainer | bjorn@buckwalter.se |
Stability | Stable |
Portability | GHC only |
Safe Haskell | None |
Language | Haskell2010 |
Extensions |
|
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)" [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 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 [1]. While the above syntax is fairly natural it is unfortunate that it must violate a number of the guidelines in [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 [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 [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 [4] to these.
Also see Samuel Hoffstaetter's blog post [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 [6] and the Fortress programming language [7] particularly noteworthy.
References
- http://physics.nist.gov/Pubs/SP811/
- http://en.wikipedia.org/wiki/Escape_velocity
- http://jscience.org/api/org/jscience/physics/models/package-summary.html
- http://www.haskell.org/haskellwiki/Physical_units
- http://liftm.wordpress.com/2007/06/03/scientificdimension-type-arithmetic-and-physical-units-in-haskell/
- http://jscience.org/
- http://research.sun.com/projects/plrg/fortress.pdf
Synopsis
- data family Dimensional v :: Dimension -> Type -> Type
- type Unit (m :: Metricality) = Dimensional (DUnit m)
- type Quantity = SQuantity One
- data Metricality
- data Dimension = Dim TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt TypeInt
- type family (a :: Dimension) * (b :: Dimension) where ...
- type family (a :: Dimension) / (d :: Dimension) where ...
- type family (d :: Dimension) ^ (x :: TypeInt) where ...
- type family NRoot (d :: Dimension) (x :: TypeInt) where ...
- type Sqrt d = NRoot d Pos2
- type Cbrt d = NRoot d Pos3
- type Recip (d :: Dimension) = DOne / d
- data Dimension' = Dim' !Int !Int !Int !Int !Int !Int !Int
- class HasDynamicDimension a => HasDimension a where
- dimension :: a -> Dimension'
- type KnownDimension (d :: Dimension) = HasDimension (Proxy d)
- (*~) :: Num a => a -> Unit m d a -> Quantity d a
- (/~) :: Fractional a => Quantity d a -> Unit m d a -> a
- (^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v)) => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a
- (^/) :: (KnownTypeInt n, Floating a) => Quantity d a -> Proxy n -> Quantity (NRoot d n) a
- (**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a
- (*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 * v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 * v2) (d1 * d2) a
- (/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 / v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 / v2) (d1 / d2) a
- (+) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
- (-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a
- negate :: Num a => Quantity d a -> Quantity d a
- abs :: Num a => Quantity d a -> Quantity d a
- signum :: Num a => Quantity d a -> Dimensionless a
- recip :: Fractional a => Quantity d a -> Quantity (Recip d) a
- nroot :: (KnownTypeInt n, Floating a) => Proxy n -> Quantity d a -> Quantity (NRoot d n) a
- sqrt :: Floating a => Quantity d a -> Quantity (Sqrt d) a
- cbrt :: Floating a => Quantity d a -> Quantity (Cbrt d) a
- exp :: Floating a => Dimensionless a -> Dimensionless a
- log :: Floating a => Dimensionless a -> Dimensionless a
- logBase :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a
- sin :: Floating a => Dimensionless a -> Dimensionless a
- cos :: Floating a => Dimensionless a -> Dimensionless a
- tan :: Floating a => Dimensionless a -> Dimensionless a
- asin :: Floating a => Dimensionless a -> Dimensionless a
- acos :: Floating a => Dimensionless a -> Dimensionless a
- atan :: Floating a => Dimensionless a -> Dimensionless a
- sinh :: Floating a => Dimensionless a -> Dimensionless a
- cosh :: Floating a => Dimensionless a -> Dimensionless a
- tanh :: Floating a => Dimensionless a -> Dimensionless a
- asinh :: Floating a => Dimensionless a -> Dimensionless a
- acosh :: Floating a => Dimensionless a -> Dimensionless a
- atanh :: Floating a => Dimensionless a -> Dimensionless a
- atan2 :: RealFloat a => Quantity d a -> Quantity d a -> Dimensionless a
- log1p :: Floating a => Dimensionless a -> Dimensionless a
- expm1 :: Floating a => Dimensionless a -> Dimensionless a
- log1pexp :: Floating a => Dimensionless a -> Dimensionless a
- log1mexp :: Floating a => Dimensionless a -> Dimensionless a
- (*~~) :: (Functor f, Num a) => f a -> Unit m d a -> f (Quantity d a)
- (/~~) :: forall f m d a. (Functor f, Fractional a) => f (Quantity d a) -> Unit m d a -> f a
- sum :: (Num a, Foldable f) => f (Quantity d a) -> Quantity d a
- mean :: (Fractional a, Foldable f) => f (Quantity d a) -> Quantity d a
- product :: (Num a, Foldable f) => f (Dimensionless a) -> Dimensionless a
- dimensionlessLength :: (Num a, Foldable f) => f b -> Dimensionless a
- nFromTo :: (Fractional a, Integral b) => Quantity d a -> Quantity d a -> b -> [Quantity d a]
- 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
- 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
- _0 :: Num a => Quantity d a
- _1 :: Num a => Dimensionless a
- _2 :: Num a => Dimensionless a
- _3 :: Num a => Dimensionless a
- _4 :: Num a => Dimensionless a
- _5 :: Num a => Dimensionless a
- _6 :: Num a => Dimensionless a
- _7 :: Num a => Dimensionless a
- _8 :: Num a => Dimensionless a
- _9 :: Num a => Dimensionless a
- pi :: Floating a => Dimensionless a
- tau :: Floating a => Dimensionless a
- siUnit :: forall d a. (KnownDimension d, Num a) => Unit NonMetric d a
- one :: Num a => Unit NonMetric DOne a
- mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a
- mkUnitQ :: Fractional a => UnitName m -> Rational -> Unit m1 d a -> Unit m d a
- mkUnitZ :: Num a => UnitName m -> Integer -> Unit m1 d a -> Unit m d a
- name :: Unit m d a -> UnitName m
- exactValue :: Unit m d a -> ExactPi
- weaken :: Unit m d a -> Unit NonMetric d a
- strengthen :: Unit m d a -> Maybe (Unit Metric d a)
- exactify :: Unit m d a -> Unit m d ExactPi
- showIn :: (Show a, Fractional a) => Unit m d a -> Quantity d a -> String
- class KnownVariant (v :: Variant) where
- dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2
- changeRep :: (KnownVariant v, Real a, Fractional b) => Dimensional v d a -> Dimensional v d b
- changeRepApproximate :: (KnownVariant v, Floating b) => Dimensional v d ExactPi -> Dimensional v d b
- asLens :: Fractional a => Unit m d a -> forall f. Functor f => (a -> f a) -> Quantity d a -> f (Quantity d a)
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 [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.
data family Dimensional v :: Dimension -> Type -> Type Source #
A dimensional value, either a Quantity
or a Unit
, parameterized by its Dimension
and representation.
Instances
type Unit (m :: Metricality) = Dimensional (DUnit m) Source #
A unit of measurement.
data Metricality Source #
Encodes whether a unit is a metric unit, that is, whether it can be combined with a metric prefix to form a related unit.
Instances
Physical Dimensions
The phantom type variable d encompasses the physical dimension of
a Dimensional
. As detailed in [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.
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'
Instances
(KnownTypeInt l, KnownTypeInt m, KnownTypeInt t, KnownTypeInt i, KnownTypeInt th, KnownTypeInt n, KnownTypeInt j) => HasDimension (Proxy (Dim l m t i th n j)) Source # | |
(KnownTypeInt l, KnownTypeInt m, KnownTypeInt t, KnownTypeInt i, KnownTypeInt th, KnownTypeInt n, KnownTypeInt j) => HasDynamicDimension (Proxy (Dim l m t i th n j)) Source # | |
Defined in Numeric.Units.Dimensional.Dimensions.TypeLevel dynamicDimension :: Proxy (Dim l m t i th n j) -> DynamicDimension Source # |
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.
type family (a :: Dimension) * (b :: Dimension) where ... infixl 7 Source #
Multiplication of dimensions corresponds to adding of the base dimensions' exponents.
type family (a :: Dimension) / (d :: Dimension) where ... infixl 7 Source #
Division of dimensions corresponds to subtraction of the base dimensions' exponents.
type family (d :: Dimension) ^ (x :: TypeInt) where ... infixr 8 Source #
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 NRoot (d :: Dimension) (x :: TypeInt) where ... Source #
Roots of dimensions corresponds to division of the base dimensions' exponents by the order of the root.
type Recip (d :: Dimension) = DOne / d Source #
The reciprocal of a dimension is defined as the result of dividing DOne
by it,
or of negating each of the base dimensions' exponents.
Term Level Representation of Dimensions
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.
data Dimension' Source #
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 Dimension
data kind.
Instances
class HasDynamicDimension a => HasDimension a where Source #
Dimensional values inhabit this class, which allows access to a term-level representation of their dimension.
dimension :: a -> Dimension' Source #
Obtains a term-level representation of a value's dimension.
Instances
HasDimension Dimension' Source # | |
Defined in Numeric.Units.Dimensional.Dimensions.TermLevel dimension :: Dimension' -> Dimension' Source # | |
HasDimension AnyUnit Source # | |
Defined in Numeric.Units.Dimensional.Dynamic dimension :: AnyUnit -> Dimension' Source # | |
HasDimension (AnyQuantity a) Source # | |
Defined in Numeric.Units.Dimensional.Dynamic dimension :: AnyQuantity a -> Dimension' Source # | |
(KnownTypeInt l, KnownTypeInt m, KnownTypeInt t, KnownTypeInt i, KnownTypeInt th, KnownTypeInt n, KnownTypeInt j) => HasDimension (Proxy (Dim l m t i th n j)) Source # | |
KnownDimension d => HasDimension (Dimensional v d a) Source # | |
Defined in Numeric.Units.Dimensional.Internal dimension :: Dimensional v d a -> Dimension' Source # |
type KnownDimension (d :: Dimension) = HasDimension (Proxy d) Source #
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
in
a context allows use of KnownDimension
d
.dimension
:: Proxy
d -> Dimension'
Dimensional Arithmetic
(*~) :: Num a => a -> Unit m d a -> Quantity d a infixl 7 Source #
Forms a Quantity
by multipliying a number and a unit.
(^) :: (Fractional a, KnownTypeInt i, KnownVariant v, KnownVariant (Weaken v)) => Dimensional v d1 a -> Proxy i -> Dimensional (Weaken v) (d1 ^ i) a infixr 8 Source #
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
.
(^/) :: (KnownTypeInt n, Floating a) => Quantity d a -> Proxy n -> Quantity (NRoot d n) a infixr 8 Source #
Computes the nth root of a Quantity
using **
.
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
.
(**) :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a infixr 8 Source #
Raises a dimensionless quantity to a dimensionless power.
(*) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 * v2), Num a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 * v2) (d1 * d2) a infixl 7 Source #
(/) :: (KnownVariant v1, KnownVariant v2, KnownVariant (v1 / v2), Fractional a) => Dimensional v1 d1 a -> Dimensional v2 d2 a -> Dimensional (v1 / v2) (d1 / d2) a infixl 7 Source #
(-) :: Num a => Quantity d a -> Quantity d a -> Quantity d a infixl 6 Source #
Subtracts one Quantity
from another.
recip :: Fractional a => Quantity d a -> Quantity (Recip d) a Source #
Forms the reciprocal of a Quantity
, which has the reciprocal dimension.
>>>
recip $ 47 *~ hertz
2.127659574468085e-2 s
nroot :: (KnownTypeInt n, Floating a) => Proxy n -> Quantity d a -> Quantity (NRoot d n) a Source #
Computes the nth root of a Quantity
using **
.
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 ^/
.
Transcendental Functions
exp :: Floating a => Dimensionless a -> Dimensionless a Source #
log :: Floating a => Dimensionless a -> Dimensionless a Source #
logBase :: Floating a => Dimensionless a -> Dimensionless a -> Dimensionless a Source #
Takes the logarithm of the second argument in the base of the first.
>>>
logBase _2 _8
3.0
sin :: Floating a => Dimensionless a -> Dimensionless a Source #
cos :: Floating a => Dimensionless a -> Dimensionless a Source #
tan :: Floating a => Dimensionless a -> Dimensionless a Source #
asin :: Floating a => Dimensionless a -> Dimensionless a Source #
acos :: Floating a => Dimensionless a -> Dimensionless a Source #
atan :: Floating a => Dimensionless a -> Dimensionless a Source #
sinh :: Floating a => Dimensionless a -> Dimensionless a Source #
cosh :: Floating a => Dimensionless a -> Dimensionless a Source #
tanh :: Floating a => Dimensionless a -> Dimensionless a Source #
asinh :: Floating a => Dimensionless a -> Dimensionless a Source #
acosh :: Floating a => Dimensionless a -> Dimensionless a Source #
atanh :: Floating a => Dimensionless a -> Dimensionless a Source #
atan2 :: RealFloat a => Quantity d a -> Quantity d a -> Dimensionless a Source #
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
log1p :: Floating a => Dimensionless a -> Dimensionless a Source #
expm1 :: Floating a => Dimensionless a -> Dimensionless a Source #
log1pexp :: Floating a => Dimensionless a -> Dimensionless a Source #
log1mexp :: Floating a => Dimensionless a -> Dimensionless a Source #
Operations on 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).
(*~~) :: (Functor f, Num a) => f a -> Unit m d a -> f (Quantity d a) infixl 7 Source #
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 infixl 7 Source #
Applies /~
to all values in a functor.
sum :: (Num a, Foldable f) => f (Quantity d a) -> Quantity d a Source #
The sum of all elements in a foldable structure.
>>>
sum ([] :: [Mass Double])
0.0 kg
>>>
sum [12.4 *~ meter, 1 *~ foot]
12.7048 m
mean :: (Fractional a, Foldable f) => f (Quantity d a) -> Quantity d a Source #
The arithmetic mean of all elements in a foldable structure.
>>>
mean [pi, _7]
5.070796326794897
product :: (Num a, Foldable f) => f (Dimensionless a) -> Dimensionless a Source #
The product of all elements in a foldable structure.
>>>
product ([] :: [Dimensionless Double])
1.0
>>>
product [pi, _4, 0.36 *~ one]
4.523893421169302
dimensionlessLength :: (Num a, Foldable f) => f b -> Dimensionless a Source #
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
:: (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] |
Returns a list of quantities between given bounds.
n <= 0 ==> nFromTo (x :: Mass Double) (y :: Mass Double) n == [x, y]
(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]
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.
type DOne = Dim Zero Zero Zero Zero Zero Zero Zero Source #
The type-level dimension of dimensionless values.
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 Source #
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.
_0 :: Num a => Quantity d a Source #
The constant for zero is polymorphic, allowing it to express zero Length
or
Capacitance
or Velocity
etc,
in addition to the Dimensionless
value zero.
_1 :: Num a => Dimensionless a Source #
_2 :: Num a => Dimensionless a Source #
_3 :: Num a => Dimensionless a Source #
_4 :: Num a => Dimensionless a Source #
_5 :: Num a => Dimensionless a Source #
_6 :: Num a => Dimensionless a Source #
_7 :: Num a => Dimensionless a Source #
_8 :: Num a => Dimensionless a Source #
_9 :: Num a => Dimensionless a Source #
pi :: Floating a => Dimensionless a Source #
tau :: Floating a => Dimensionless a Source #
Twice pi
.
For background on tau
see http://tauday.com/tau-manifesto (but also
feel free to review http://www.thepimanifesto.com).
Constructing Units
siUnit :: forall d a. (KnownDimension d, Num a) => Unit NonMetric d a Source #
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.
one :: Num a => Unit NonMetric DOne a Source #
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 [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.
mkUnitR :: Floating a => UnitName m -> ExactPi -> Unit m1 d a -> Unit m d a Source #
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.
Unit Metadata
exactValue :: Unit m d a -> ExactPi Source #
weaken :: Unit m d a -> Unit NonMetric d a Source #
Discards potentially unwanted type level information about a Unit
.
Pretty Printing
On Functor
, and Conversion Between Number Representations
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.
class KnownVariant (v :: Variant) where Source #
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.
extractValue, extractName, injectValue, dmap
dmap :: (a1 -> a2) -> Dimensional v d a1 -> Dimensional v d a2 Source #
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.
Instances
KnownVariant (DQuantity s) Source # | |
Defined in Numeric.Units.Dimensional.Internal extractValue :: Dimensional (DQuantity s) d a -> (a, Maybe ExactPi) extractName :: Dimensional (DQuantity s) d a -> Maybe (UnitName NonMetric) injectValue :: Maybe (UnitName NonMetric) -> (a, Maybe ExactPi) -> Dimensional (DQuantity s) d a dmap :: (a1 -> a2) -> Dimensional (DQuantity s) d a1 -> Dimensional (DQuantity s) d a2 Source # | |
Typeable m => KnownVariant (DUnit m) Source # | |
Defined in Numeric.Units.Dimensional.Internal extractValue :: Dimensional (DUnit m) d a -> (a, Maybe ExactPi) extractName :: Dimensional (DUnit m) d a -> Maybe (UnitName NonMetric) injectValue :: Maybe (UnitName NonMetric) -> (a, Maybe ExactPi) -> Dimensional (DUnit m) d a dmap :: (a1 -> a2) -> Dimensional (DUnit m) d a1 -> Dimensional (DUnit m) d a2 Source # |
changeRep :: (KnownVariant v, Real a, Fractional b) => Dimensional v d a -> Dimensional v d b Source #
Convenient conversion between numerical types while retaining dimensional information.
>>>
let x = (37 :: Rational) *~ poundMass
>>>
changeRep x :: Mass Double
16.78291769 kg
changeRepApproximate :: (KnownVariant v, Floating b) => Dimensional v d ExactPi -> Dimensional v d b Source #
Convenient conversion from exactly represented values while retaining dimensional information.
Lenses
These functions are compatible with the lens library.