kudzu-forest/elm-units-in-the-wild/1.0.0

NOTE: the latest version is 1.0.2

Units

This module provides functionalities for parsing and displaying SI units (and some other daily-use units). This module has 4 groups of functionalities that is for

  1. creating Unit values
  2. parsing String into Unit
  3. displaying Unit as String in various form.
  4. simple arithmetics of Quantity = (Float, Unit).

Types

type Prefix

Type representing SI prefix ("kilo" in "kilometer" etc.)

Type that represents unit before prefixed, like "meter" in "millimeter".

type Unit

Type that represents unit

type Error

Represents all recoverable errors that may occur during unit parsing, conversion, or arithmetic operations.

Defining New Unit Symbol

{ parserInput : List String
, display : String
, baseSIExpression :
{ meterExponent : Int
, kilogramExponent : Int
, secondExponent : Int
, kelvinExponent : Int
, ampereExponent : Int
, moleExponent : Int
, candelaExponent : Int
, numericalValue : Result String Float
}
, allowPrefix : Bool
}

Creates a UnitSymbol, which represents an unprefixed unit such as "meter" in "millimeter".

This function is mainly intended for defining custom unit symbols that can later be added to ValidatedUnitSymbols for parsing.

Creation of Unit

Unit with no dimension. Equals to 1.

Creates Unit value from UnitSymbol with no prefix added.

u pascal
|> toString defaultDisplayOption
--> "Pa"

Creates Unit value from Prefix and UnitSymbol.

pu kilo pascal
|> toString defaultDisplayOption
--> "kPa"
fromList : List ( Prefix, UnitSymbol, Int ) -> Unit

Creates Unit value from List ( Prefix, UnitSymbol, Int ), where the Int value represents exponent. The order in the list is preserved and used as the displayed order. In complex case where you want to use / or parened exponentiation like kg (m / s)^2, other functions may be more suited.

fromList [(centi, meter, 1), (noPrefix, second, -1)]
|> toString defaultDisplayOption
--> "cm s^-1"

Represents multiplication of units.

u meter |> times (inverse (u second))
|> toString defaultDisplayOption
--> "m s^-1"
per : Unit -> Unit -> Unit

Represents division with slash (not with negative exponent). Intended to be used with |> operator. Note that |> is left associative.

u joule |> per (u kelvin) |> times (u gram)
|> toString defaultDisplayOption
--> "(J / K) g"


u joule |> per ((u kelvin) |> times (u gram))
|> toString defaultDisplayOption
--> "J / (K g)"


u joule |> per (u kelvin) |> per (u gram)
|> toString defaultDisplayOption
--> "(J / K) / g"


u joule |> times (inverse (u kelvin)) |> per (u gram)
|> toString defaultDisplayOption
--> "J K^-1 / g"
toThe : Int -> Unit -> Unit

Represents exponentiation of units. Intended to be used with |> operator. Note that |> is left associative.

u mole |> per (pu deci meter |> toThe 3) |> toThe 2
|> toString defaultDisplayOption
--> "(mol / dm^3)^2"

Syntax suger for toThe 2.

square (u meter)
|> toString defaultDisplayOption
--> "m^2"

Syntax suger for toThe 3.

cubic (pu deci meter)
|> toString defaultDisplayOption
--> "dm^3"

Syntax suger for toThe -1. Note that this is different from power at display.

u mole |> times (inverse (u liter)) |> per (u second)
|> toString defaultDisplayOption
--> "mol L^-1 / s"

Adds paren to unit. This affects displayed string.

Simplifies unit expressions by applying semantic normalization, such as:

  • (s^-1)^2 is converted into s^-2.
  • m / 1, m 1 or m^1 are all converted into m.
    litterInSI : Unit
    litterInSI = cubic (pu deci meter)

    square litterInSI
    |> toString defaultDisplayOption
    --> "(dm^3)^2"

    square litterInSI
    |> simplifyUnitExpression
    |> toString defaultDisplayOption
    --> "dm^6"

Parsing

Unit parsing with this module proceeds in following pathway.

  • Defining set of units used in parser.
    • To avoid unit name collision, this process goes through ValidatedUnitSymbols type, whose creation may fail with name-conflicting units.
  • Converting ValidatedUnitSymbols into parser by toParser function.
    • This should be done only once at initialization process, because growing ValidatedUnitSymbols and creating parser is relatively heavy operation.
  • Using that parser with parse function.

ValidatedUnitSymbols

You can:

  • use the default set of unit symbols (defaultUnitSymbols) provided by this module,
  • define new unit symbols as needed with createUnitSymbol and add them to the default set using addUnitSymbols,
  • or build your own set of unit symbols from scratch by folding addUnitSymbols over a list, starting from emptyUnitSymbols.

Some units (e.g. minute, inch, year, atm) are defined as non-prefixable units to avoid ambiguity with SI prefixes.

Represents unit symbols usables in parsing purpose, in which no name collision could occurs. This type is exposed so that users can control which unit symbols are accepted by the parser.

Represents vacant ValidatedUnitSymbols value

Represents ValidatedUnitSymbols value with SI standarized units and some other daily use unit.

The only way to add a user-defined unit symbol for parsing purpose.

This function fails if the new unit symbol conflicts with existing symbols or their prefixed forms.

    eV : UnitSymbol
    eV =
        createUnitSymbol
            { display = "eV"
            , parserInput = ["eV"]
            , baseSIExpression =
                { meterExponent = 2
                , kilogramExponent = 1
                , secondExponent = -2
                , kelvinExponent = 0
                , ampereExponent = 0
                , moleExponent = 0
                , candelaExponent = 0
                , numericalValue =
                    Ok (1.602176634e-19)
                }
            , allowPrefix = True
            }


    parser : Parser
    parser =
         defaultUnitSymbols
            |> addUnitSymbols [eV]
            |> Result.map toParser
            |> Result.withDefault failureParser


    parsedUnit : Unit
    parsedUnit =
        case parse parser "keV" of
            Ok unit ->
                unit
            Err _ ->
                emptyUnit




    (200, parsedUnit)
        |> convertInto (u eV)
        --> Ok (toFloat (round 200000), u eV)

Getting and Using Parser

Converts a set of unit symbols into a Parser.

If you want to build a parser that accepts only SI base units (with or without prefixes), you can write:

baseUnitsParser : Parser
baseUnitsParser =
    emptyUnitSymbols
        |> addUnitSymbols
            [ meter, gram, second, kelvin, ampere, mole, candela ]
        |> Result.map toParser
        |> Result.withDefault failureParser

This gives you a parser that rejects any non-SI or user-defined unit symbols.

parse baseUnitsParser "cm s^-1"
    |> Result.map (toString defaultDisplayOption)
    --> Ok "cm s^-1"


parse baseUnitsParser "km / h"
    |> Result.map (toString defaultDisplayOption)
    |> Result.withDefault "failed to parse!"
    --> "failed to parse!"
failureParser : Parser

This parser always fails. Intended to be used as a fallback when addUnitSymbols fails, which indicates a configuration error rather than a runtime parsing error.

parse : Parser -> String -> Result (List DeadEnd) Unit

Runs the parser onto given string.

type alias Parser =
Parser Unit

Type alias for Parser.Parser Unit from elm/parser.

type alias DeadEnd =
DeadEnd

Type alias for Parser.DeadEnd from elm/parser.

Display

toString : DisplayOption -> Unit -> String

Converts unit into string with specified display options.

Display Settings

Type that represents configurations for displaying units as strings. This includes options about:

  • output format
    • for now, 3 options are available.
      • ascii characters like um s^-1 (This is default value)
      • unicode characters like μm s⁻¹
      • tex string like {\\rm{{\\mu}{m}}\\,/\\,{s^3}}.
  • unit separator in arbitrary string ( default value is " ")
  • how dimensionless unit should be displayed ( default value is "[-]")
  • In which way one |> per (u second) should be expressed, 1 / s or / s.
  • (for tex user) the outermost slash symbol could be expressed in \\frac{<numerator>}{<denominator>} style.

default values for DisplayOption

default values for DisplayOption tuned for TeX output.

Change the output format into ascii string

Change the output format into tex string

Change the output format into unicode string

Change the unit separator into arbitrary string.

Change the slash into arbitrary string.

Change the heading slash (like "/ " in "6.02×10²³ / mol") into arbitrary string.

{ open : String
, close : String
}

Change the paren into arbitrary string.

Change the expression of empty unit(1) into arbitrary string.

Display like \\frac{{\\rm{mol}}\\,/\\,{\\rm{L}}}{{\\rm{s}}}. This enforces displayTopNumeratorOne to be applied.

Display like \\left({\\rm{mol}}\\,/\\,{\\rm{L}}\\right)\\,/\\,{\\rm{s}}

display like "K /(mol / kg)". Note that space between "/" and "(" are removed.

cancels useCustomSpacing.

Arithmetics

Note on unit growth

Units are combined syntactically when multiplying or dividing quantities. This means repeated multiplications or divisions may result in large unit expressions (e.g. m * s * s).

This is by design.

If you want to keep unit expressions compact or convert them into a specific unit, call convertInto or normalize explicitly at appropriate points in your calculation.

Checks if two arguments have the same dimension.

u liter |> hasSameDimension (cubic (u meter))
--> True


u liter |> hasSameDimension (square (u meter))
--> False
type alias Quantity =
( Float, Unit )

Representing phisical quantity that is unit multiplied by numerical coefficient.

convertInto : Unit -> Quantity -> Result Error Quantity

Converts expression of physical quantity with unit into another unit. This fails when:

  • The targeted unit and start unit has different dimensions.

  • Units like ℃, Sv or year is involved.

    (3, cubic (u meter))
        |> convertInto (u liter)
        |> Result.map
            (\(numericalValue, unit) ->
                    String.fromInt (round numericalValue)
                    ++ " "
                    ++ toString defaultDisplayOption unit
            )
        --> Ok "3000 L"
normalize : Quantity -> Result Error Quantity

Converts quantity into normalized unit in the form of m^a kg^b s^c K^d A^e mol^f cd^g, exponent 1 and unit with 0 exponent being omitted. The numerical value is also converted accordingly.

add : Quantity -> Quantity -> Result Error Quantity

Adds two quantities. Fails if unit for either of addend contains inconvertible unit like ℃, or two addeds have different dimensions.

subtractBy : Quantity -> Quantity -> Result Error Quantity

Subtracts one quantity from another. Fails if unit for either subtrahend or minuend contains inconvertible unit like ℃, or subtrahend and minuend have different dimensions.

multiplyBy : Quantity -> Quantity -> Result Error Quantity

Multiply one quantity by another. Fails if unit for either of two contains inconvertible unit like ℃.

divideBy : Quantity -> Quantity -> Result Error Quantity

Divides one quantity by another. Fails if unit for either of two contains inconvertible unit like ℃.

List (Quantity -> Result Error Quantity)
-> Quantity
-> Result Error Quantity

Applies transformations sequentially, stopping at the first error.

Predefined Unit Symbols

The following sections list predefined unit symbols and prefixes. They are primarily reference material and are not required for understanding the core API.

Some units (e.g. minute, inch, year, atm) are defined as non-prefixable units to avoid ambiguity with SI prefixes.

Unit for length denoted as m.

Unit for mass denoted as g.

Unit for time denoted as s.

Unit for absolute temperature denoted as K.

Unit for electric current denoted as A.

Unit for amount of substance denoted as mol.

Unit for luminous intensity denoted as cd.

Unit for mass denoted as ton. Equals to 10^6 kg.

Unit for time denoted as min. Equals to 60 s.

Unit for time denoted as h. Equals to 3600 s.

Unit for time denoted as day. Equals to 86400 s.

Unit for time denoted as year. The exact definition may vary depending on context. In this package, conversion involving this unit is not supported.

Unit for force denoted as N. Equals to kg m s^-2.

Unit for pressure denoted as Pa. Equals to N m^-2.

Unit for pressure denoted as atm. Equals to 1.01325×10^5 Pa.

Unit for pressure denoted as mmHg. Equals to 1/760 atm.

Unit for pressure denoted as torr. Equals to mmHg.

Unit for pressure denoted as bar. Equals to 10^5 Pa.

Unit for work and energy denoted as J. Equals to N・m.

Unit for power denoted as W. Equals to J / s.

Unit for temperature denoted as ℃. A temperature of t ℃ is equal to (t + 273.15) K. In this package, conversion involving this unit is not supported.

Unit for frequency denoted as Hz. Equals to s^-1

Unit for electricity denoted as C. Equals to A・s.

Unit for voltage denoted as V. Equals to J / C.

Unit for resistance denoted as Ω. Equals to V / A.

Unit for conductivity denoted as S. Equals to A / V.

Unit for capacity denoted as F. Equals to C / V.

Unit for magnetic flux denoted as Wb. Equals to J / A.

Unit for magnetic flux density denoted as T. Equals to Wb / m^2.

Unit for self or mutual inductance denoted as H. Equals to V / (A s^-1).

Unit for luminous flux denoted as Lm. Equals to cd・sr.

Unit for illuminance denoted as Lx. Equals to Lm / m^2.

Unit for radio activity denoted as Bq. Equals to 1 / s.

Unit for absorbed dose denoted as Gy. Equals to J / kg.

Unit for exposure dose denoted as Sv. The dimension equals to J / kg, but with different weight coefficient applied to different radio beam kind.

Unit for volume denoted as L. Equals to 10^(-3) m^3.

Unit for angle denoted as °. Equals to (π/180) rad.

Unit for angle denoted as rad.

Unit for stereo angle denoted as sr.

Unit for length denoted as in.

Unit for length denoted as yd.

Unit for mass denoted as lb.

Prefix

Prefix that represents 10^(-30)

Prefix that represents 10^(-27)

Prefix that represents 10^(-24)

Prefix that represents 10^(-21)

Prefix that represents 10^(-18)

Prefix that represents 10^(-15)

Prefix that represents 10^(-12)

Prefix that represents 10^(-9)

Prefix that represents 10^(-6)

Prefix that represents 10^(-3)

Prefix that represents 10^(-2)

Prefix that represents 10^(-1)

Prefix type value that shows nothing is prefixed.

Prefix that represents 10^(1)

Prefix that represents 10^(2)

Prefix that represents 10^(3)

Prefix that represents 10^(6)

Prefix that represents 10^(9)

Prefix that represents 10^(12)

Prefix that represents 10^(15)

Prefix that represents 10^(18)

Prefix that represents 10^(21)

Prefix that represents 10^(24)

Prefix that represents 10^(30)