I got curious and installed the CLI tool[1] and found that it does indeed account for leap second / leap years:
>>> let jahr: Time = 1 year
>>> let tage: Time = jahr -> days
>>> tage
= 365.243 day [Time]
References:Time is nasty because there’s lots of overloaded concepts and bugs hide in the implicit conversions between meanings.
I’m also kinda curious what the underlying type is. Is it a double or do they use arbitrary precision math and take the perf hit.
A year has two meanings - a calendar year, with all the leap days and seconds and timezones, or a duration of time. The latter is still useful, e.g. when someone states that Proxima Centauri is 4.2 light-years away, they don't want to deal with leap-days.
Decent time libraries have separate ways to deal with durations and dates.
Per 400 years, there is one leap day every 4 years (100 leap days), except when the year is divisible by 100 (so we overcounted by 4 and there are 100 – 4 = 96 leap days), except when the year is divisible by 400 (so we need to add that day back and arrive at 100 – 4 + 1 = 97). This gives us 97/400 = 0·2425.
The tropical year is about 365·24219 days long, but that's not relevant to timekeeping.
Yes. This is also how it is defined: https://github.com/sharkdp/numbat/blob/ba9e97b1fbf6353d24695...
The calculation above is showing a rounded result (6 significant digits by default).
I've seen from to time some empirical equations that result to this. Usually, it's just hand-waved away and the non-integer units are ignored or dropped when the transcendental function is evaluated. I guess the trivial answer is probably add an extra factor with a value of 1 with the reciprocal units to result to the said type 1 for dimensionless units?
[1]: https://numbat.dev
the issue is that each column and each row of a matrix can have different units. worse, gauss-jordan elimination chooses which rows to operate on dynamically. there is eventually a solution to this problem in c++ far down the comments thread
i don't see anything in https://numbat.dev/doc/type-system.html about aggregate types such as vectors, matrices, maps, arrays, lists, trees, records, etc., though it does have a suggestively named scalar type. i wonder how it handles this sort of thing
Note that it is possible to construct a type system solution to this problem (vectors/matrices with non-uniform units). A colleague of mine has an excellent talk on this: https://www.youtube.com/watch?v=SLSTS-EvOx4
By 'Scalar', in the document you referenced, we mean a dimensionless quantity. Not a scalar in the scalar-vector-matrix-tensor sense. Maybe I should rethink that notation.
i know it's possible (even in c++ apparently) but so far it seems difficult
In the article, he states: "Let's call the matrix of all (xi 1) `X` and let's call the vector of all yi `Y`", and then states that the units of `X` are (m 1).
But if you instead say the units of `X` are just m, the "1" is in units "m", then the problem goes away, the whole matrix has the same units ("m"), and everything works fine.
I'll be honest in that I've never thought of a matrix having multiple units per column... and I can't think of any example where you aren't just putting the units in the wrong place. Let me know if you have a concrete example that might work.
Also most of economics consists of linear systems with non homogeneous units. The first entry could be bushels of wheat, the next demand for steel, etc.
basically it's very very common
And it is not open source.
>>> 1 day + 1 month
1 day + 1 month
= 31.4369 day >>> 1 month -> seconds
= 2_629_746 s [Time]
Just playing around with this and really enjoying it.If the author is around, I notice in the README you mention the GNU units program, which I use quite a bit. I'm curious if you've made any notable divergences from it?
I don't think we diverge from units on purpose anywhere, but I am probably not very familiar with its syntax, to be honest. I did however look at its huge collection of units and checked if there were any important units missing. I think we have a pretty comprehensive list of units that are supported by now in Numbat [1].
One thing I find unintuitive is the implementation of variable length units (like months or years) as an averaged constant. Then again, perhaps that’s the point - and e.g. working with months without specifying a calendar is just unintuitive.
Using averaged constants like this isn't applicable to all use-cases, but has the virtues of being simple and easy to reason about.
IMO they did exactly the right thing by not trying to shoehorn an endless supply of complexity and pain into their language's type system. Users who do need to deal with date math can use a library.
My guess is that it has support for time, but not for calendars. The fact that a "month" is a static length points in this direction.
Maybe there's going to be a calendar module in the future though?
https://github.com/sharkdp/numbat/blob/786512175b99c195a7d5b...
https://benjaminjurke.com/content/articles/2015/compile-time...
Why not both? Make lightyears both Length and Time
https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...
Julia has a neat little library called Unitful.jl[1] which does almost exactly what Numbat does by taking advantage of Julia's extremely flexible type system. Extending it to cover all of Numbat's functionality could be trivially accomplished in a few lines. In fact, fun type magic like this is pretty much my primary motivation for using Julia in the first place.
Not that I want to detract from the author's hard work, of course, I'm just very excited about Julia! Numbat is obviously an awesome project, and I can't wait to see where it leads!
> Extending it to cover all of Numbat's functionality could be trivially accomplished in a few lines.
Look, I'm not claiming that Numbat is superior. But I think Numbat might have its own little niche. And even if not, it's always good to have alternative solutions available.
- Numbat is specifically designed to handle physical dimensions and units. It has a special syntax to give developers the best experience when dealing with units. You can just type "KiB" and have it be parsed as kibibytes. You can use the "->" operator to convert to other units. You can directly annotate functions with physical dimensions ("fn kinetic_energy(m: Mass, v: Velocity) -> Energy = …"). Other languages and their unit libraries might have the same functionality, but it has always been added as an afterthought.
- Numbat has a static type system, Julia's is dynamic.
- Numbat can be compiled to Web Assembly and you can run it in your browser. I'm not sure if that is possible with Julia?
- Numbat might eventually be able to infer (physical dimension) function parameter types with its Hindley-Milner-style type system [1]. The only language that I know of that can do this is F#. The author of F#'s unit system wrote his PhD thesis on the subject [2], and Numbat follows the original approach in the thesis quite closely.
It's fine these days. https://tshort.github.io/WebAssemblyCompiler.jl/stable/examp...
I think the biggest issue with unit systems though is that algorithms are not generally unitful. I develop the Julia SciML solvers (https://docs.sciml.ai/Overview/stable/) and there's certain pieces like the initial dt calculation of an ODE solver which are not canonically correct in a unitful sense. So there's certain pieces where you have to opt out of unit checks which gets a bit messy. But other than that Unitful.jl is fine, and that's an algorithmic thing not a unit package thing.
It even pretty much looks identical to those Numbat snippets!
import unchained
let earth_mass = 5.972168e24.kg
let solar_mass = 1.9885e30.kg
let lunar_mass = 7.342e22.kg
let distance_sun = 1.AU # astronomical unit
let distance_moon = 384_400.km
let force_sun = G_Newton * earth_mass * solar_mass / distance_sun
let force_moon = G_Newton * earth_mass * lunar_mass / distance_moon
echo force_sun / force_moon
# 69593.6 UnitLess
Sorry for the shameless plug. ;) Numbat looks quite cool though and the article talks about a lot of things to think about when writing such a program / lib / programming language.how does unchained handle gaussian elimination
x²
x÷y
We do have infix unicode operators though. So `x ÷ y` (spacing required though!) could be implemented easily. I use this for `±` in Measuremancer [0] (which btw also supports Unchained units, for error propagation on unitful measurements).
You missed the point about this example though. I wanted to show how Numbat can help prevent the exact error that you made in your program. 'G_Newton * earth_mass * solar_mass / distance_sun' is not a force. It's an energy. How would you write type annotations in this Nim example to help you find that same mistake?
In this case to get compiler help it's pretty much identical to Numbat. Annotating the "force" variables with a `Force` type. There's a few technicalities involved though. Normally the user is supposed to use explicit type annotations for variables. Quantities like `Force`, `Energy` etc. are mainly supported for function arguments (they are "concepts" in Nim lingo, a specific version of generics).
Technically you can abuse these concepts for type checking, by annotating with `Force`, e.g. `let force_sun: Force = ...`. That will correctly raise a CT error about mismatching units in this case. However, the code will /also/ not compile if you type `Energy` due to the underspecified type.
So what you're supposed to do is:
import unchained
let earth_mass = 5.972168e24.kg
let solar_mass = 1.9885e30.kg
let lunar_mass = 7.342e22.kg
let distance_sun = 1.AU # astronomical unit
let distance_moon = 384_400.km
let force_sun: N = G_Newton * earth_mass * solar_mass / distance_sun
let force_moon: N = G_Newton * earth_mass * lunar_mass / distance_moon
echo force_sun / force_moon
which will correctly raise a Error: type mismatch: got 'Joule' for '...' but expected 'Newton = Alias'
(or any other unit of force; note that if it was a non SI unit, e.g. `eV` for an energy, you'd need to explicitly convert the RHS expression into `eV` using `.to(eV)`. That will perform the CT check of whether the conversion is valid, and if so, hands you the value in `eV`. Implicit conversions to explicitly given types is not supported)So I think we more or less do the same things. :)
And just to clarify, I'm always happy to see more libraries / programs etc. that do units as types. But for the same reason you hadn't even heard about Unchained, is the reason I can't stop myself from mentioning it in such a context. :D (niche programming language + niche topic clearly doesn't help).
Frink runs on the JVM and is also available on Android.
I use it as a general purpose calculator on my smartphone.
It's really nice that Numbat is written in Rust :) Will have to try it out.
True: https://frinklang.org/faq.html#OpenSource
Thanks for sharing Numbat with us.
It looks great!
https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gnat_ugn_unw/Perfor...
If rust ever gets more complete const generics, then things like tiny-uom might work.
- you define addition and subtraction between objects of one type returning an object of the same type;
- you define multiplication and division as returning an object of a type with the dimensions summed or subtracted;
- and you get dimensional correctness via type safety.
It has units awareness --- not necessarily only physical units.
For example; light, temperature and time are measured in units but they are not physical units --- aka 3 dimensional.
https://phys.org/news/2012-04-physicists-abolish-fourth-dime...