units-2.4: A domain-specific type system for dimensional analysis

Copyright(C) 2014 Richard Eisenberg
LicenseBSD-style (see LICENSE)
MaintainerRichard Eisenberg (eir@cis.upenn.edu)
Stabilityexperimental
Portabilitynon-portable
Safe HaskellNone
LanguageHaskell2010

Data.Metrology.TH

Description

This module exports Template Haskell functions to make working with units a little more convenient.

Synopsis

Documentation

evalType :: Q Type -> Q Type Source

Evaluates a type as far as it can. This is useful, say, in instance declarations:

instance Show $(evalType [t| Length |]) where ...

Without the evalType, the instance declaration fails because Length mentions type families, which can't be used in instance declarations.

This function is somewhat experimental, and will likely not work with more polymorphic types. (If it doesn't work, not all of the type families will be evaluated, and the instance declaration will fail. This function should never cause incorrect behavior.)

declareDimension :: String -> Q [Dec] Source

Declare a new dimension of the given name:

$(declareDimension "Length")

produces

data Length = Length
instance Dimension Length

declareCanonicalUnit :: String -> Q Type -> Maybe String -> Q [Dec] Source

declareCanonicalUnit unit_name dim (Just abbrev) creates a new canonical unit (that is, it is not defined in terms of other known units) named unit_name, measuring dimension dim. abbrev will be the abbreviation in the unit's Show instance. If no abbraviation is supplied, then no Show instance will be generated.

Example usage:

$(declareCanonicalUnit "Meter" [t| Length |] (Just "m"))

declareDerivedUnit :: String -> Q Type -> Rational -> Maybe String -> Q [Dec] Source

declareDerivedUnit unit_name base_unit_type ratio (Just abbrev) creates a new derived unit, expressed in terms of base_unit_type. ratio says how many base units are in the derived unit. (Thus, if unit_name is Minute and base_unit_type is ''Second, then ratio would be 60.) abbrev, if supplied, becomes the string produced in the derived unit's Show instance. If no abbreviation is supplied, no Show instance is generated.

Example usage:

$(declareDerivedUnit "Minute" [t| Second |] 60 (Just "min"))

declareMonoUnit :: String -> Maybe String -> Q [Dec] Source

declareMonoUnit unit_name (Just abbrev) creates a new derived unit, intended for use without unit polymorphism. The same type stands for both the unit and dimension, and the instance of DefaultUnitOfDim is set up accordingly. Use this function (with the Metrology imports) if you don't want to bother with LCSUs and just want to get to work. The abbrev, if supplied, creates an appropriate Show instance.

$(declareMonoUnit "Meter" (Just "m"))

produces all of the following

data Meter = Meter
instance Dimension Meter
instance Unit Meter where
  type BaseUnit Meter = Canonical
  type DimOfUnit Meter = Meter
type instance DefaultUnitOfDim Meter = Meter
instance Show Meter where
  show _ = "m"

After a declaration like this, you probably want

type Length = MkQu_U Meter

This last line is not generated, as it is easy enough for you to write, and it involves a new name (Length).

declareConstant :: String -> Rational -> Q Type -> Q [Dec] Source

declareConstant const_name value unit_type creates a new numerical constant, named const_name. Its numerical value is value expressed in units given by unit_type. The constant is polymorphic in both its LCSU and numerical representation. For example,

declareConstant "gravity_g" 9.80665 [t| Meter :/ Second :^ Two |]

yields

gravity_g :: ( Fractional n
             , CompatibleUnit lcsu (Meter :/ Second :^ Two) )
          => MkQu_ULN (Meter :/ Second :^ Two) lcsu n
gravity_g = 9.80665 % (undefined :: Meter :/ Second :^ Two)