Public verbs for combining and converting quantities. All are plain vars (autocomplete-friendly, no clojure.core shadowing) and interoperate with plain numbers as dimensionless scalars.
(require '[commensura.units :as u] '[commensura.core :refer [by per plus minus pow to ratio]])
(to (by (u/feet 10) (u/feet 12) (u/feet 8)) u/gallons) ;=> 552960/77 gallon ≈ 7181.30 [volume] ; str/println form; prn wraps it in a ; #commensura/quantity tagged literal
Comparison verbs — the everyday lt?/le?/gt?/ge?/eq?/ne? (a physical,
dimension-checked order that throws on overlapping intervals) plus the interval-aware
certainly-…?/possibly-…? operators — order quantities by base magnitude, so
(eq? (u/inch 12) (u/foot 1)) is true.
The verbs also accept Intervals (see commensura.interval); an interval accessor treats a bare quantity/number as a degenerate interval, so no scalar promotion is needed.
Public verbs for combining and converting quantities. All are plain vars
(autocomplete-friendly, no clojure.core shadowing) and interoperate with plain
numbers as dimensionless scalars.
(require '[commensura.units :as u]
'[commensura.core :refer [by per plus minus pow to ratio]])
(to (by (u/feet 10) (u/feet 12) (u/feet 8)) u/gallons)
;=> 552960/77 gallon ≈ 7181.30 [volume] ; str/println form; prn wraps it in a
; #commensura/quantity tagged literal
Comparison verbs — the everyday `lt?`/`le?`/`gt?`/`ge?`/`eq?`/`ne?` (a physical,
dimension-checked order that throws on overlapping intervals) plus the interval-aware
`certainly-…?`/`possibly-…?` operators — order quantities by base magnitude, so
`(eq? (u/inch 12) (u/foot 1))` is true.
The verbs also accept Intervals (see commensura.interval); an interval accessor treats a
bare quantity/number as a degenerate interval, so no scalar promotion is needed.Historical U.S. purchasing power (CPI). A period dollar is a unit worth
(CPI_current / CPI_period) · dollar, so it composes with the core verbs and, because two of
them share the dollar basis, to between them converts across years — inflation-adjustment:
(require '[commensura.cpi :as cpi] '[commensura.core :as c] '[commensura.units :as u]) (c/to (u/dollar 1250) (cpi/usd 1913)) ; 1913 dollars → current dollars (c/to (c/by 50 (cpi/cent 1955 11)) (cpi/usd 1985 10)) ;=> 2.020446096654275 (exact)
Data (BLS CPI-U series CPIAUCNS, via FRED) ships as resources/commensura/cpi.edn — exact
rationals; :current is the latest shipped period. dev/commensura/cpi/{fetch,frink}.clj
regenerate it (FRED API) / a Frink-parity oracle fixture. Live refresh is M4.2.
Historical U.S. purchasing power (CPI). A *period dollar* is a unit worth
`(CPI_current / CPI_period) · dollar`, so it composes with the core verbs and, because two of
them share the `dollar` basis, `to` between them converts across years — inflation-adjustment:
(require '[commensura.cpi :as cpi]
'[commensura.core :as c]
'[commensura.units :as u])
(c/to (u/dollar 1250) (cpi/usd 1913)) ; 1913 dollars → current dollars
(c/to (c/by 50 (cpi/cent 1955 11)) (cpi/usd 1985 10)) ;=> 2.020446096654275 (exact)
Data (BLS CPI-U series CPIAUCNS, via FRED) ships as `resources/commensura/cpi.edn` — exact
rationals; `:current` is the latest shipped period. `dev/commensura/cpi/{fetch,frink}.clj`
regenerate it (FRED API) / a Frink-parity oracle fixture. Live refresh is M4.2.Fetch CPI (BLS series CPIAUCNS) from the FRED API and build the cpi data map — shared by the dev
refresh task (commensura.cpi.fetch) and the runtime live source (commensura.cpi). HTTP+JSON via
commensura.http. Values are kept exact rationals (CPIAUCNS is not seasonally adjusted, so it is
never revised). Fetch/parse failures throw an ex-info tagged :cpi/source-error so callers can fall
back to the shipped data.
Fetch CPI (BLS series CPIAUCNS) from the FRED API and build the cpi data map — shared by the dev refresh task (`commensura.cpi.fetch`) and the runtime live source (`commensura.cpi`). HTTP+JSON via `commensura.http`. Values are kept **exact rationals** (CPIAUCNS is not seasonally adjusted, so it is never revised). Fetch/parse failures throw an ex-info tagged `:cpi/source-error` so callers can fall back to the shipped data.
Discoverable currency units — live rates via CurrencyFreaks (needs CURRENCYFREAKS_API_KEY).
One fn per code: (EUR) => 1 EUR, (EUR 600) => 600 EUR, (EUR 3 5) => 15 EUR^2 (arity ->
exponent, as for a unit); compose with the core verbs, e.g. (c/to (EUR 600) u/dollar). Codes
without a legal symbol name, and dynamic use, go through commensura.currency.rates/of.
Discoverable currency units — live rates via CurrencyFreaks (needs CURRENCYFREAKS_API_KEY). One fn per code: `(EUR)` => 1 EUR, `(EUR 600)` => 600 EUR, `(EUR 3 5)` => 15 EUR^2 (arity -> exponent, as for a unit); compose with the core verbs, e.g. `(c/to (EUR 600) u/dollar)`. Codes without a legal symbol name, and dynamic use, go through `commensura.currency.rates/of`.
Live currency exchange rates from CurrencyFreaks (USD base). The machinery behind the generated
commensura.currency unit fns: fetch (cached, TTL) → an exact code -> USD-value mapping → a unit.
Live-only: rates aren't shipped (they're volatile and proprietary), so this needs the user's own
key — set CURRENCYFREAKS_API_KEY (a free tier exists). A missing key or a fetch failure throws.
Rates are kept exact (rationals); each currency is worth (usd-value code) · dollar.
Live currency exchange rates from CurrencyFreaks (USD base). The machinery behind the generated `commensura.currency` unit fns: fetch (cached, TTL) → an exact `code -> USD-value` mapping → a unit. Live-only: rates aren't shipped (they're volatile and proprietary), so this needs the user's own key — set `CURRENCYFREAKS_API_KEY` (a free tier exists). A missing key or a fetch failure throws. Rates are kept exact (rationals); each currency is worth `(usd-value code) · dollar`.
GENERATED dimension-name index (dims-map -> name) from units.edn's ||| declarations — see commensura.units.gen. Consulted by quantity's printer.
GENERATED dimension-name index (dims-map -> name) from units.edn's ||| declarations — see commensura.units.gen. Consulted by quantity's printer.
Read-only discovery over the registered units and dimensions — "what units have this
dimension?", "search unit names", "what is this value?". These are queryable views
over the registry (commensura.registry): every fn reflects live registrations and
returns plain data — sorted vectors of names, or maps — never the internal atoms. Reach
here instead of filtering the registry atoms by hand.
(require '[commensura.discover :as d] '[commensura.units :as u]) (d/search-units "volt") ;=> ["volt" "abvolt" "intvolt" "statvolt" "thermalvolt" "electronvolt"] (d/units-of-dimension u/foot) ;=> ["actus" "angstrom" … "foot" … "meter" … "mile" …] (all lengths) (d/describe (c/per u/mile u/hour)) ;=> {:value "1 mile/hour [velocity]" :dimensions {:length 1 :time -1} :dimension-name "velocity"}
Read-only discovery over the registered units and dimensions — "what units have this
dimension?", "search unit names", "what is this value?". These are *queryable views*
over the registry (`commensura.registry`): every fn reflects live registrations and
returns plain data — sorted vectors of names, or maps — never the internal atoms. Reach
here instead of filtering the registry atoms by hand.
(require '[commensura.discover :as d] '[commensura.units :as u])
(d/search-units "volt") ;=> ["volt" "abvolt" "intvolt" "statvolt" "thermalvolt" "electronvolt"]
(d/units-of-dimension u/foot) ;=> ["actus" "angstrom" … "foot" … "meter" … "mile" …] (all lengths)
(d/describe (c/per u/mile u/hour)) ;=> {:value "1 mile/hour [velocity]" :dimensions {:length 1 :time -1} :dimension-name "velocity"}Shared HTTP+JSON helpers for the live data sources (FRED CPI, CurrencyFreaks). http-kit for the
request, clojure.data.json for the body. Failures throw an ex-info tagged :remote-error so
callers can decide whether to fall back.
Shared HTTP+JSON helpers for the live data sources (FRED CPI, CurrencyFreaks). http-kit for the request, `clojure.data.json` for the body. Failures throw an ex-info tagged `:remote-error` so callers can decide whether to fall back.
A frinj-flavoured notation for commensura — an optional, familiar entry point for people coming
from the frinj library (Martin Trojer's Clojure port of Alan Eliasen's Frink). It is a thin sugar
layer: every value it builds is an ordinary commensura.quantity, so the exact tower, dimensions, and
printing all carry through — a conversion keeps its unit and dimension rather than flattening to a bare
number the way frinj does.
(require '[commensura.infix :refer [fj $= to]])
(fj 10 :feet 12 :feet 8 :feet :to :gallons) ;=> 552960/77 gallon ≈ 7181.30 [volume]
($= (fj 2 :tons) / (fj 10 :feet 12 :feet :water))
; a value you can keep computing with — no str needed
Two forms:
fj — keyword soup. A left-to-right product of alternating numbers and :unit keywords
((fj 5 12 :floz) ⇒ 60 floz); :per divides the next factor; :to converts everything after it to
a target built the same way. Unit keywords resolve through commensura.registry — with a plural
fallback (:gallons → gallon), since commensura registers singular names.$= — infix math over ** * / + - (→ pow/by/per/plus/minus) and the comparisons
== != < > <= >= (→ eq?/ne?/lt?/gt?/le?/ge?), with precedence ** > * / > + - >
comparisons. Add your own with defop. Operands are fj forms, numbers, or nested $=.to (a plain fn) converts an already-built quantity: (to q :dollars :per :day). A unit-led target
re-expresses in that unit (keeps the dimension); a number-led target — (to keg 12 :floz) — returns
the dimensionless count; a mirrored target whose dimension is the reciprocal of the source's is
flipped for you (frinj's "reverse mirrored units"), so (to fuel-per-distance :feet :per :gallon)
yields the economy. (This reversal is the frinj-flavoured layer's; commensura.core/to stays strict.)
Define your own units the ordinary commensura way — (defunit beer (fj 12 :floz 3.2 :percent :water :per :alcohol)) — and later soups resolve them by name ((fj :magnum 13.5 :percent :to :beer)); this
layer adds no separate registration path (frinj's add-unit! is just commensura.core/defunit).
Credit: the notation and the worked examples are Alan Eliasen's Frink and Martin Trojer's frinj; this namespace just re-points them at commensura's exact engine.
A **frinj-flavoured** notation for commensura — an optional, familiar entry point for people coming
from the `frinj` library (Martin Trojer's Clojure port of Alan Eliasen's Frink). It is a thin sugar
layer: every value it builds is an ordinary `commensura.quantity`, so the exact tower, dimensions, and
printing all carry through — a conversion keeps its unit and dimension rather than flattening to a bare
number the way frinj does.
(require '[commensura.infix :refer [fj $= to]])
(fj 10 :feet 12 :feet 8 :feet :to :gallons) ;=> 552960/77 gallon ≈ 7181.30 [volume]
($= (fj 2 :tons) / (fj 10 :feet 12 :feet :water))
; a value you can keep computing with — no `str` needed
Two forms:
* **`fj`** — *keyword soup*. A left-to-right product of alternating numbers and `:unit` keywords
(`(fj 5 12 :floz)` ⇒ 60 floz); `:per` divides the next factor; `:to` converts everything after it to
a target built the same way. Unit keywords resolve through `commensura.registry` — with a plural
fallback (`:gallons` → `gallon`), since commensura registers singular names.
* **`$=`** — *infix math* over `** * / + -` (→ `pow`/`by`/`per`/`plus`/`minus`) and the comparisons
`== != < > <= >=` (→ `eq?`/`ne?`/`lt?`/`gt?`/`le?`/`ge?`), with precedence `**` > `* /` > `+ -` >
comparisons. Add your own with **`defop`**. Operands are `fj` forms, numbers, or nested `$=`.
`to` (a plain fn) converts an already-built quantity: `(to q :dollars :per :day)`. A *unit-led* target
re-expresses in that unit (keeps the dimension); a *number-led* target — `(to keg 12 :floz)` — returns
the dimensionless **count**; a *mirrored* target whose dimension is the reciprocal of the source's is
flipped for you (frinj's "reverse mirrored units"), so `(to fuel-per-distance :feet :per :gallon)`
yields the economy. (This reversal is the frinj-flavoured layer's; `commensura.core/to` stays strict.)
Define your own units the ordinary commensura way — `(defunit beer (fj 12 :floz 3.2 :percent :water
:per :alcohol))` — and later soups resolve them by name (`(fj :magnum 13.5 :percent :to :beer)`); this
layer adds no separate registration path (frinj's `add-unit!` is just `commensura.core/defunit`).
Credit: the notation and the worked examples are Alan Eliasen's Frink and Martin Trojer's frinj; this
namespace just re-points them at commensura's exact engine.Interval arithmetic over conforming Quantities. An interval denotes every value between its endpoints; arithmetic yields the tightest interval containing every result (the interval-inclusion property). Endpoints are Quantities (or plain numbers), kept exact.
Two shapes, unified by the IInterval protocol (lo/hi/main-value):
Interval2 [lo hi] — a plain range.Interval3 [lo main hi] — a range with a main value (Frink's mainValue): a
best-known estimate that need not be the center. It propagates through arithmetic (the
same op applied to the operands' mains) and is dropped the moment any operand lacks one,
so [2,2.5,3] * [7,8.2,9.4] = [14, 20.5, 28.2].Per Frink, a bare number/Quantity is not an interval. Only the two records implement
IInterval; the *-or-identity accessors add the degenerate case (mainValue[5]=5) by
returning a non-interval unchanged (its own bound), which is why the arithmetic and the
comparison operators need no scalar 'promotion'. interval? is false for a scalar.
An interval prints as its raw record (endpoints as their own #commensura/quantity
literals) — no bespoke tagged literal — which is readable and round-trips losslessly.
The commensura.core verbs (by/per/plus/minus/pow/to/ratio) accept intervals
transparently.
Interval arithmetic over conforming Quantities. An interval denotes every value between
its endpoints; arithmetic yields the tightest interval containing every result (the
interval-inclusion property). Endpoints are Quantities (or plain numbers), kept exact.
Two shapes, unified by the `IInterval` protocol (`lo`/`hi`/`main-value`):
* `Interval2 [lo hi]` — a plain range.
* `Interval3 [lo main hi]` — a range with a *main value* (Frink's `mainValue`): a
best-known estimate that need not be the center. It propagates through arithmetic (the
same op applied to the operands' mains) and is dropped the moment any operand lacks one,
so `[2,2.5,3] * [7,8.2,9.4] = [14, 20.5, 28.2]`.
Per Frink, a bare number/Quantity is *not* an interval. Only the two records implement
`IInterval`; the `*-or-identity` accessors add the degenerate case (`mainValue[5]=5`) by
returning a non-interval unchanged (its own bound), which is why the arithmetic and the
comparison operators need no scalar 'promotion'. `interval?` is false for a scalar.
An interval prints as its raw record (endpoints as their own `#commensura/quantity`
literals) — no bespoke tagged literal — which is readable and round-trips losslessly.
The `commensura.core` verbs (`by`/`per`/`plus`/`minus`/`pow`/`to`/`ratio`) accept intervals
transparently.Math functions over quantities and intervals — the ones that carry dimensions.
(require '[commensura.math :as m]) (m/sqrt (c/pow (u/meter 1) 2)) ;=> 1 meter (exact when a perfect root) (m/abs (u/meter -5)) ;=> 5 meter (m/min (u/foot 1) (u/inch 6)) ;=> 6 inch (physical order, keeps the unit) (m/mod (u/hour 25) (u/hour 24));=> 1 hour
Only functions that touch dimensions live here — preserving them (abs/mod/min/max/
floor/ceil/round), scaling them (sqrt/root/pow), or crossing the boundary (sign:
dimensioned → dimensionless). Transcendentals (exp/ln/sin/…) are intentionally absent:
they only ever map dimensionless → dimensionless, so they belong to plain numeric code.
Over intervals, the monotone functions lift by mapping the endpoints — sign/floor/ceil/round
directly, abs with a special case when the interval spans zero — and sqrt/root/pow scale
through. mod/rem are the exception: scalar-only, and they reject an interval argument, because
modular reduction is discontinuous and cannot be soundly lifted ([23,25] mod 24 is {23} ∪ [0,1],
not a single interval). Names shadow clojure.core, so use this namespace qualified (m/abs), never
:refer. Comparisons come from commensura.core.
Math functions over quantities and intervals — the ones that *carry dimensions*.
(require '[commensura.math :as m])
(m/sqrt (c/pow (u/meter 1) 2)) ;=> 1 meter (exact when a perfect root)
(m/abs (u/meter -5)) ;=> 5 meter
(m/min (u/foot 1) (u/inch 6)) ;=> 6 inch (physical order, keeps the unit)
(m/mod (u/hour 25) (u/hour 24));=> 1 hour
Only functions that touch dimensions live here — preserving them (`abs`/`mod`/`min`/`max`/
`floor`/`ceil`/`round`), scaling them (`sqrt`/`root`/`pow`), or crossing the boundary (`sign`:
dimensioned → dimensionless). Transcendentals (`exp`/`ln`/`sin`/…) are intentionally absent:
they only ever map dimensionless → dimensionless, so they belong to plain numeric code.
Over intervals, the monotone functions lift by mapping the endpoints — `sign`/`floor`/`ceil`/`round`
directly, `abs` with a special case when the interval spans zero — and `sqrt`/`root`/`pow` scale
through. `mod`/`rem` are the exception: **scalar-only, and they reject an interval argument**, because
modular reduction is discontinuous and cannot be soundly lifted (`[23,25] mod 24` is `{23} ∪ [0,1]`,
not a single interval). Names shadow `clojure.core`, so use this namespace qualified (`m/abs`), never
`:refer`. Comparisons come from `commensura.core`.The value types.
A Unit is a named registered unit — a name, an exact base-SI magnitude, and
a stored dimension map. foot, meter, newton, beer are all Units; a bare
Unit is one of itself and prints foot [length]. defunit mints Units.
A Quantity is an anonymous computed value — an exact magnitude plus an
ordered display formula (a vector of UnitTerms). Its dimensions are derived
from the formula, never stored, so they can't disagree with it. Every exact
arithmetic result is a Quantity.
An ApproxQuantity is the same as a Quantity but its magnitude is an
arbitrary-precision BigDecimal — for irrational values (planck units,
semitone, Richter) that have no exact rational form. Arithmetic promotes:
the moment any operand is approximate, the result is an ApproxQuantity and the
magnitude math runs under the caller's *math-context* (with-precision /
binding), defaulting to DECIMAL128. Values print under two tags —
#commensura/unit and #commensura/quantity — with a leading ≈ in the payload
marking approximate values, so inexactness is never mistaken for exact.
So: named ⇒ Unit (stores dims), anonymous ⇒ Quantity/ApproxQuantity (store a
formula); dims live in exactly one place. All are callable and implement
Dimensionable/Measured/Formulaic.
The value types. A `Unit` is a *named* registered unit — a name, an exact base-SI magnitude, and a stored dimension map. `foot`, `meter`, `newton`, `beer` are all Units; a bare Unit is one of itself and prints `foot [length]`. `defunit` mints Units. A `Quantity` is an *anonymous* computed value — an exact magnitude plus an ordered display `formula` (a vector of `UnitTerm`s). Its dimensions are *derived* from the formula, never stored, so they can't disagree with it. Every exact arithmetic result is a Quantity. An `ApproxQuantity` is the same as a `Quantity` but its magnitude is an arbitrary-precision `BigDecimal` — for *irrational* values (planck units, `semitone`, Richter) that have no exact rational form. Arithmetic *promotes*: the moment any operand is approximate, the result is an `ApproxQuantity` and the magnitude math runs under the caller's `*math-context*` (`with-precision` / `binding`), defaulting to DECIMAL128. Values print under two tags — `#commensura/unit` and `#commensura/quantity` — with a leading `≈` in the payload marking approximate values, so inexactness is never mistaken for exact. So: named ⇒ Unit (stores dims), anonymous ⇒ Quantity/ApproxQuantity (store a formula); dims live in exactly one place. All are callable and implement `Dimensionable`/`Measured`/`Formulaic`.
Reifies the #commensura/quantity "…" and #commensura/unit "…" tagged
literals (registered in resources/data_readers.clj). A quantity literal rebuilds
an anonymous Quantity from its display formula; a unit literal resolves the named
Unit through the registry (so user defunits reify too).
A leading ≈ in the payload marks an approximate value: for those, the BigDecimal
in the literal is the source of truth (no re-check). For an exact quantity the exact
value is the source of truth — its printed approximation is re-derived and checked
against the literal's, throwing if they disagree beyond *approx-tolerance* (ruling
out junk data or values that would come out differently under JVM/library drift).
Reifies the `#commensura/quantity "…"` and `#commensura/unit "…"` tagged literals (registered in resources/data_readers.clj). A quantity literal rebuilds an anonymous Quantity from its display formula; a unit literal resolves the named Unit through the registry (so user `defunit`s reify too). A leading `≈` in the payload marks an *approximate* value: for those, the BigDecimal in the literal is the source of truth (no re-check). For an *exact* quantity the exact value is the source of truth — its printed approximation is re-derived and checked against the literal's, throwing if they disagree beyond `*approx-tolerance*` (ruling out junk data or values that would come out differently under JVM/library drift).
Global tables, in the spirit of Frink's single global namespace:
defunit registers every unit here (builtins
auto-register when commensura.units loads); the #commensura/quantity
reader resolves unit names here, so user-defined units reify too.register-unit-resolver! installs a pred/dispatch pair
that builds a whole family of names on demand (e.g. dollar_1960); resolve-unit
tries the table first, then resolvers, so families reify from the reader without
being registered one-by-one.commensura.dimensions/names (the ||| labels) and extended at runtime
via register-dimension!, so users can name their own dimensions.The two tables share a symmetric API — register-{unit,dimension}! (last-writer-
wins, warning when a name is redefined to a different value), lookup-{unit, dimension}, all-{units,dimensions}, clear-{units,dimensions}!.
The backing atoms (units, dim-names, unit-resolvers) are public — reach for
them directly when you want bulk / swap! / add-watch / reordering access. The
functions above are the ergonomic path that also applies the invariants (redefine
warnings, dimension-key normalization, reseed-on-clear); the atoms trust you to know
what you're doing.
Trade-off (documented): this is global mutable state. Unit vars remain lexically
namespaced and unaffected — only the string-keyed lookup / literal reification path is
global, and redefining a name via register-*! warns (last writer wins). For cold
deserialization, the defining defunit (or a resolver) must be loaded before a literal
that names its unit is read.
Global tables, in the spirit of Frink's single global namespace:
* a name -> unit table — `defunit` registers every unit here (builtins
auto-register when `commensura.units` loads); the `#commensura/quantity`
reader resolves unit names here, so *user*-defined units reify too.
* unit *resolvers* — `register-unit-resolver!` installs a `pred`/`dispatch` pair
that builds a whole *family* of names on demand (e.g. `dollar_1960`); `resolve-unit`
tries the table first, then resolvers, so families reify from the reader without
being registered one-by-one.
* a dimension-map -> human-name table — seeded from the generated
`commensura.dimensions/names` (the `|||` labels) and extended at runtime
via `register-dimension!`, so users can name their own dimensions.
The two tables share a symmetric API — `register-{unit,dimension}!` (last-writer-
wins, warning when a name is redefined to a *different* value), `lookup-{unit,
dimension}`, `all-{units,dimensions}`, `clear-{units,dimensions}!`.
The backing atoms (`units`, `dim-names`, `unit-resolvers`) are **public** — reach for
them directly when you want bulk / `swap!` / `add-watch` / reordering access. The
functions above are the ergonomic path that also applies the invariants (redefine
warnings, dimension-key normalization, reseed-on-clear); the atoms trust you to know
what you're doing.
Trade-off (documented): this is global mutable state. Unit *vars* remain lexically
namespaced and unaffected — only the string-keyed lookup / literal reification path is
global, and redefining a name via `register-*!` warns (last writer wins). For cold
deserialization, the defining `defunit` (or a resolver) must be loaded before a literal
that names its unit is read.The Richter scale — a nonlinear (log/exp) conversion between an earthquake's
magnitude and its radiated seismic energy, ported from Frink's Richter[n]
function (Choy–Boatwright 1995). Because the scale is logarithmic, every result
is irrational ⇒ an ApproxQuantity.
(require '[commensura.richter :as r] '[commensura.core :refer [to]]) (r/magnitude->energy 7.0) ;=> ~2.0e15 joule (r/energy->magnitude (r/magnitude->energy 7.0)) ;=> ~7.0 richter
richter is a dimensionless marker unit, so a computed magnitude prints as
6.9 richter [dimensionless] rather than a bare number.
The Richter scale — a nonlinear (log/exp) conversion between an earthquake's magnitude and its radiated seismic energy, ported from Frink's `Richter[n]` function (Choy–Boatwright 1995). Because the scale is logarithmic, every result is irrational ⇒ an `ApproxQuantity`. (require '[commensura.richter :as r] '[commensura.core :refer [to]]) (r/magnitude->energy 7.0) ;=> ~2.0e15 joule (r/energy->magnitude (r/magnitude->energy 7.0)) ;=> ~7.0 richter `richter` is a dimensionless marker unit, so a computed magnitude prints as `6.9 richter [dimensionless]` rather than a bare number.
Dependency-free fuzzy name matching for "did you mean?" hints. A restricted Damerau-Levenshtein (optimal string alignment) ranker: given a misspelling and a pool of candidate names, return the closest few. Used to enrich the reader's unknown-unit error, and reusable anywhere a name lookup misses.
Dependency-free fuzzy name matching for "did you mean?" hints. A restricted Damerau-Levenshtein (optimal string alignment) ranker: given a misspelling and a pool of candidate names, return the closest few. Used to enrich the reader's unknown-unit error, and reusable anywhere a name lookup misses.
Absolute temperature-scale conversions — Frink's affine Fahrenheit/Celsius/Reaumur
functions (K = scale·x + offset). Each is exact, and dispatches on its argument like
Frink's Name[x]:
(celsius 100) ;=> 373.15 K (a dimensionless reading → absolute temperature) (fahrenheit 32) ;=> 273.15 K (celsius (fahrenheit 32));=> 0 (a temperature → its reading on this scale)
These are ABSOLUTE temperatures — distinct from the multiplicative degree units
u/degcelsius / u/degreeFahrenheit, which are temperature differences (ΔT):
(celsius 100) is 373.15 K, but (u/degcelsius 100) is a 100 K difference. (Rankine
has zero offset, so it is a plain unit, u/Rankine; no affine fn is needed.)
Absolute temperature-scale conversions — Frink's affine `Fahrenheit`/`Celsius`/`Reaumur` functions (`K = scale·x + offset`). Each is exact, and dispatches on its argument like Frink's `Name[x]`: (celsius 100) ;=> 373.15 K (a dimensionless reading → absolute temperature) (fahrenheit 32) ;=> 273.15 K (celsius (fahrenheit 32));=> 0 (a temperature → its reading on this scale) These are ABSOLUTE temperatures — distinct from the multiplicative *degree* units `u/degcelsius` / `u/degreeFahrenheit`, which are temperature *differences* (ΔT): `(celsius 100)` is 373.15 K, but `(u/degcelsius 100)` is a 100 K difference. (Rankine has zero offset, so it is a plain unit, `u/Rankine`; no affine fn is needed.)
Generated callable unit vars — see commensura.units.gen. Use via :as u.
Generated callable unit vars — see commensura.units.gen. Use via :as u.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |