A fixed list of quantities, each with its facts: a base unit, a kind, bounds, additivity, and display families by unit system (spec/03 §8–§13).
A quantity is what a unit measures. A unit (a UCUM code, Macaw.Data.Unit) and a
quantity together are a measure, %{unit: code, quantity: quantity}, the two facts a
field description carries. They are facts about the data, not knobs: a measure does
not change a value, it says what the value already is, so a display can choose how to
show it.
The list is fixed and grows by proposal: duration, information, data_rate,
frequency, ratio, count, temperature, length, speed, power, energy,
pressure, area, volume, mass, pace, angle, currency,
electric_potential, electric_current, electric_charge, illuminance,
sound_level, mass_concentration and amount_concentration. spec/03 §8–§13 tables each
one's facts and families.
- Kind is
:linear,:affine(a temperature),:logarithmic(a sound level) or:cyclic(an angle, whose mean is circular). - Bounds are a display hint, never validation: a difference is legitimately negative, so a value outside them is never refused.
- Additivity depends on the aggregation:
additivity/2refines the quantity's defaults across series and over time by the field's metric kind (:gauge,:counter,:rate). - Unit systems are
:metric,:us,:ukand:as_posted; a system picks the family a value is shown in and never touches the data.
Information is decimal by default: UCUM's kBy and Telemetry.Metrics'
:kilobyte are 1,000 bytes, and a unit written with a binary prefix (MiBy) or in
bit belongs to its own family. The data picks it; no setting does.
How a value is drawn in its family (the display unit, tick formats, a value's label in
a locale) is the drawing library's: visualize's Visualize.Quantity keeps it, over this
vocabulary.
The vocabulary half of visualize's Visualize.Quantity.
Examples
iex> Macaw.Data.Quantity.family(:mass, "kg", :us)
["[oz_av]", "[lb_av]"]
iex> Macaw.Data.Quantity.additivity(:data_rate)
%{across: true, over_time: false}
iex> Macaw.Data.Quantity.label(%{unit: "{request}"})
"request"
Summary
Types
How a quantity's values combine (spec/03 §9).
A unit and a quantity, either or both; the two facts a field description carries.
How a field's values were measured, which refines its quantity's additivity.
Why a measure does not resolve.
A unit system: a display preference, never applied to the data.
One of the fixed list of quantities.
Functions
Whether a sum of the quantity's values means anything (spec/03 §10, D-155):
%{across: boolean, over_time: boolean} — summing several series at one instant, and
summing one series over time. The quantity's physical defaults are refined by the
field's metric kind: a :gauge and a :rate never add over time, and a :counter's
increments add over time when the quantity adds across. No quantity (a plain number)
defaults to both.
The fixed list of quantities (spec/03 §8).
A quantity's base unit, the unit its values are in when no unit is given; nil for a
:currency, whose code the data must say.
Where a quantity's values physically lie, in its base unit, {min | nil, max | nil}
(spec/03 §9): a display hint an axis may anchor at, never validation — a value
outside them is never refused.
A quantity's bounds in a unit of it: the base's, converted. A unit without a factor
(Cel) takes the base's bounds as they are.
The display family a unit of the quantity belongs to, smallest first; [] for a unit
of no family's shape (spec/03 §11). Within the unit's convention (family/3), a unit
belongs to the first family holding a member of its shape — its atoms and exponents,
prefixes aside and a binary prefix kept apart — so the unit picks among a quantity's
families: bits, binary prefixes, hertz, watt-hours, US lengths. No unit means the base
unit.
The display family a system shows a unit of the quantity in (spec/03 §11, D-158): the
family the unit's convention names for :metric, :us or :uk when it names one,
else the unit's own (family/2), which is what :as_posted always is.
How a quantity's values combine (spec/03 §9): :linear, :affine (a temperature),
:logarithmic (a sound level) or :cyclic (an angle).
The label rule (spec/03 §14): the unit, else the quantity, else nothing — the
unit's symbol with its annotations written when it is not empty, else the quantity's
name with spaces for underscores, else nil. Raises ArgumentError for a measure
resolve/1 refuses.
The quantity a code's dimension implies, or nil when none of the list has it. A
dimensionless code holding % is a :ratio, any other a :count, and a code of one
currency alone ({iso4217:USD}) a :currency.
A measure completed and checked (spec/03 §13): a unit alone implies its quantity, a
quantity alone means its base unit, and a unit whose dimension is not its quantity's is
refused. A :ratio or a :count takes any dimensionless unit, and a :currency a
code of one currency alone.
The unit systems a display may prefer (spec/03 §12), :as_posted the default.
Types
@type kind() :: :linear | :affine | :logarithmic | :cyclic
How a quantity's values combine (spec/03 §9).
@type measure() :: %{ optional(:unit) => Macaw.Data.Unit.code() | nil, optional(:quantity) => t() | nil }
A unit and a quantity, either or both; the two facts a field description carries.
@type metric_kind() :: :gauge | :counter | :rate
How a field's values were measured, which refines its quantity's additivity.
@type reason() :: {:unit, Macaw.Data.Unit.code(), Macaw.Data.Unit.reason()} | {:quantity, t(), Macaw.Data.Unit.code()} | {:unknown_quantity, term()}
Why a measure does not resolve.
@type system() :: :metric | :us | :uk | :as_posted
A unit system: a display preference, never applied to the data.
@type t() ::
:duration
| :information
| :data_rate
| :frequency
| :ratio
| :count
| :temperature
| :length
| :speed
| :power
| :energy
| :pressure
| :area
| :volume
| :mass
| :pace
| :angle
| :currency
| :electric_potential
| :electric_current
| :electric_charge
| :illuminance
| :sound_level
| :mass_concentration
| :amount_concentration
One of the fixed list of quantities.
Functions
@spec additivity(t() | nil, metric_kind() | nil) :: %{ across: boolean(), over_time: boolean() }
Whether a sum of the quantity's values means anything (spec/03 §10, D-155):
%{across: boolean, over_time: boolean} — summing several series at one instant, and
summing one series over time. The quantity's physical defaults are refined by the
field's metric kind: a :gauge and a :rate never add over time, and a :counter's
increments add over time when the quantity adds across. No quantity (a plain number)
defaults to both.
Examples
iex> Macaw.Data.Quantity.additivity(:count, :gauge)
%{across: true, over_time: false}
iex> Macaw.Data.Quantity.additivity(:temperature, :counter)
%{across: false, over_time: false}
@spec all() :: [t(), ...]
The fixed list of quantities (spec/03 §8).
@spec base_unit(t()) :: Macaw.Data.Unit.code() | nil
A quantity's base unit, the unit its values are in when no unit is given; nil for a
:currency, whose code the data must say.
Where a quantity's values physically lie, in its base unit, {min | nil, max | nil}
(spec/03 §9): a display hint an axis may anchor at, never validation — a value
outside them is never refused.
Examples
iex> Macaw.Data.Quantity.bounds(:duration)
{0, nil}
@spec bounds(t(), Macaw.Data.Unit.code()) :: {number() | nil, number() | nil}
A quantity's bounds in a unit of it: the base's, converted. A unit without a factor
(Cel) takes the base's bounds as they are.
Examples
iex> Macaw.Data.Quantity.bounds(:ratio, "%")
{0.0, 100.0}
@spec family(t(), Macaw.Data.Unit.code() | nil) :: [Macaw.Data.Unit.code()]
The display family a unit of the quantity belongs to, smallest first; [] for a unit
of no family's shape (spec/03 §11). Within the unit's convention (family/3), a unit
belongs to the first family holding a member of its shape — its atoms and exponents,
prefixes aside and a binary prefix kept apart — so the unit picks among a quantity's
families: bits, binary prefixes, hertz, watt-hours, US lengths. No unit means the base
unit.
Examples
iex> Macaw.Data.Quantity.family(:information, "MiBy")
["By", "KiBy", "MiBy", "GiBy", "TiBy"]
iex> Macaw.Data.Quantity.family(:length, "[ft_i]")
["[in_i]", "[ft_i]", "[mi_i]"]
@spec family(t(), Macaw.Data.Unit.code() | nil, system()) :: [Macaw.Data.Unit.code()]
The display family a system shows a unit of the quantity in (spec/03 §11, D-158): the
family the unit's convention names for :metric, :us or :uk when it names one,
else the unit's own (family/2), which is what :as_posted always is.
A quantity read in several conventions — a pressure is gauge, barometric or clinical —
picks the unit's by its exact code first, then by its shape, else the first, so a
system chooses within a convention and never across: a barometric hPa is shown in
inches of mercury under :us, and a blood pressure in mm[Hg] stays in it under every
system.
Examples
iex> Macaw.Data.Quantity.family(:mass, "kg", :uk)
["[oz_av]", "[lb_av]", "[stone_av]"]
iex> Macaw.Data.Quantity.family(:duration, "ms", :us)
["ns", "us", "ms", "s", "min", "h", "d"]
iex> Macaw.Data.Quantity.family(:pressure, "hPa", :us)
["[in_i'Hg]"]
How a quantity's values combine (spec/03 §9): :linear, :affine (a temperature),
:logarithmic (a sound level) or :cyclic (an angle).
Examples
iex> Macaw.Data.Quantity.kind(:angle)
:cyclic
The label rule (spec/03 §14): the unit, else the quantity, else nothing — the
unit's symbol with its annotations written when it is not empty, else the quantity's
name with spaces for underscores, else nil. Raises ArgumentError for a measure
resolve/1 refuses.
Examples
iex> Macaw.Data.Quantity.label(%{unit: "ms"})
"ms"
iex> Macaw.Data.Quantity.label(%{quantity: :count})
"count"
iex> Macaw.Data.Quantity.label(%{})
nil
@spec of_unit(Macaw.Data.Unit.code()) :: t() | nil
The quantity a code's dimension implies, or nil when none of the list has it. A
dimensionless code holding % is a :ratio, any other a :count, and a code of one
currency alone ({iso4217:USD}) a :currency.
Examples
iex> Macaw.Data.Quantity.of_unit("kW.h")
:energy
iex> Macaw.Data.Quantity.of_unit("{request}")
:count
@spec resolve(measure()) :: {:ok, %{unit: Macaw.Data.Unit.code() | nil, quantity: t() | nil}} | {:error, reason()}
A measure completed and checked (spec/03 §13): a unit alone implies its quantity, a
quantity alone means its base unit, and a unit whose dimension is not its quantity's is
refused. A :ratio or a :count takes any dimensionless unit, and a :currency a
code of one currency alone.
Examples
iex> Macaw.Data.Quantity.resolve(%{quantity: :duration})
{:ok, %{unit: "s", quantity: :duration}}
iex> Macaw.Data.Quantity.resolve(%{unit: "By", quantity: :duration})
{:error, {:quantity, :duration, "By"}}
@spec systems() :: [system(), ...]
The unit systems a display may prefer (spec/03 §12), :as_posted the default.