But assuming your comment is not a joke. You probably want to convert minutes to seconds in order to work with the same units, then add the scalar parts together.
That's how you deal with different quantities: convert to same unit, add values.
This is analog to fractions: 1/2 + 1/4 = 2/4 + 1/4 = (2+1)/4 = 3/4.
In basic middle school math it’s common to multiply different units as a basic conversion mechanism. Multiplying by the same unit is semantically equivalent to “x times 1 is identity(x)”, and other cross-unit arithmetic implies conversion to ensure like units before processing. A typed unit numeric system would imply that to me. It would not imply I’m multiplying the units, but rather the scalar value of the unit.
> In basic middle school math it’s common to multiply different units as a basic conversion mechanism
EDIT: Yes, you multiply the units `2m * 2s` : you first multiply the units to get: `m.s`. This is what I say: you convert everything to the same units before doing the calculations.
> Multiplying by the same unit is semantically equivalent to “x times 1 is identity(x)”
This is wrong.
1kg * 1kg = 1kg² period.
What you're saying is `2kg * 1 = 2kg`, which is right, because `1` is a scalar while `2kg` is a quantity. This is completely different than multiplying 2 quantities.
> It would not imply I’m multiplying the units, but rather the scalar value of the unit.
That's where you're wrong. When doing arithmetic on quantities, you have 2 equations:
x = 2kg * 4s
unit(x) = kg * s = kg.s
scalar(x) = 2 * 4 = 8
x = 8 kg.s
Or
x = 5m / 2s
x = (5/2) m/s
x = 2.5 m/s
There is a meaning to units and the operation you do with them. `5m / 2s` is 5 meters in 2 seconds, which is the speed `2.5 m/s`.
`2m + 1s` has no meaning, therefore you can't do anything with the scalar values, and the result remains `2m + 1s`, not `3 (m+s)`.
All unit conversions are actually multiplications by the dimensionless constant 1, i.e., no-ops.
Let's say that you want to convert `2 min` into seconds. You know that `1 min = 60 s` is true. Dividing this equation by `1 min` on both sides is allowed and brings `1 = (60 s) / (1 min)`. This shows that if we multiply any value in minutes by `(60 s) / (1 min)`, we are not actually changing the value, because this is equivalent to multiplying it by 1. Therefore, `2 min = 2 min * 1 = 2 min * (60 s) / (1 min) = 2 * 60 s * (1 min) / (1 min) = 120 s`. We didn't change the value because we multiplied it by 1, and we didn't change its dimensionality ("type") because we multiplied it by a dimensionless number. We just moved around a dimensionless factor of 60, from the unit to the numerical value.
I think that you misremember, or didn't realize that to convert minutes into seconds, you were not multiplying by `60 s` but by `(60 s) / (1 min)` which is nothing else than 1.
Units can be expressed in terms of other units, and you can arbitrarily pick one unit as a base and then express the rest in it. But the key word here is "arbitrarily".
If multiplying by the same unit yield the same unit, then how did you compute area or volume in school?
Wait, would you really expect 1m * 1m to be anything other than 1m²? When does it ever happens that you want to multiply to non-unitless[1] measurements and not multiply the units???
I expect 1 * m = 1m, and 1 * m * m = 1m because applying a unit doesn’t inherently have a value of that unit associated with it. (1 m) (1 m) obviously equals 1m^2, but ((1 m) m) is not the same expression.
If you look upthread, there was a mention of F# unit types. Taking off my programmer hat and returning to my middle school anecdote which also evidently made no sense: expression of a unit without a value is (or should be to my mind, based on my education) a cast, not a computation of N+1 values.
- 1 is unitless
- 1 * m casts the value to a value 1 of unit m = 1m
- 1 * m * m casts the value 1 * m = 1m to 1m then casts 1m to m which = 1m
Admittedly my educational background here might be wildly unconventional but it certainly prepared me for interoperable unit types as a concept without changing values (~precision considerations).
> If you look upthread, there was a mention of F# unit types.
And the syntax is `3<unit>` not `3 * unit`
- 1 is a scalar
- 1m is a quantity
- 2 * 1m "casts" 2 to a meter, but really this is just multiplying a quantity by a scalar
- 2 * 1m * 1m "casts" 2 to meter², multiplying 2 quantities then by a scalar
I insist, `1 * m` does not make sense. This is not a valid mathematical expression, because a unit can never be on its own without a value.
> expression of a unit without a value is (or should be to my mind, based on my education) a cast
There is no casting in math. Mainly because there is no types, only objects with operations. A vector is not a scalar and you can't cast it into a scalar.
A quantity is not a scalar either, and you can't cast one into another.
A quantity is an object, you can multiply 2 quantities together, but you can't add them if they are different. You can multiply a quantity to a scalar, but you still can't add a scalar to a quantity.
Well, yeah, F# represents this at the type level. Which I’ve said elsewhere in the discussion is preferable. Not knowing Go, but knowing it only recently gained generics, I read multiplying by `time.Seconds` (which does not have a visible 1 associated with it) as perhaps performing an operator-overloaded type cast to a value/type with the Seconds unit assigned to it. I’ve since learned that Go also does not support operator overloading, so I now know that wouldn’t be the case. But had that been the case, it isn’t inconceivable that unitlessValue * valuelessUnit * valuelessUnit = unitlessValue * valuelessUnit. Because…
> I insist, `1 * m` does not make sense. This is not a valid mathematical expression, because a unit can never be on its own without a value.
Well, if you insist! But you seem to be imposing “mathematical expression” on an expression space where that’s already not the case? Whatever you may think of operator overloading, it is a thing that exists and it is a thing that “makes sense” to people using it idiomatically.
Even in languages without overloading, expressions which look like maths don’t necessarily have a corresponding mathematical representation. An equals infix operator in maths is a statement, establishing an immutable fact. Some languages like Erlang honor this, many (most? I strongly suspect most) don’t! I couldn’t guess without researching it also treat infix = statements as an expression which evaluated to a value.
The syntax of infix operators is generally inspired by mathematical notation, but it’s hardly beholden to that. The syntax of programming languages generally is not beholden to mathematical notation. Reacting as if it’s impossibly absurd that someone might read 1 * time.Seconds * time.Seconds as anything other than 1 * 1s * 1s is just snobbery.
Not knowing Go, I focused on the syntax and the explicit values, and tried to build a syntax tree on top of it. I’m not a fan of infix operators, and I am a fan of lisps, so my mental syntax model was (* (* 1 time.Seconds) time.Seconds)), which still doesn’t “make sense” mathematically, but it can make sense if `*` is a polymorphic function which accepts unquantified units.
> Not knowing Go, but knowing it only recently gained generics, I read multiplying by `time.Seconds` (which does not have a visible 1 associated with it) as perhaps performing an operator-overloaded type cast to a value/type with the Seconds unit assigned to it.
This sums up your incomprehension. `time.Seconds` is just a constant. An integer with the value `1_000_000` meaning 1 million of nanoseconds.
In an expression of the form `a * b` you should always read `a` and `b` as constants. This is true for EVERY programming language.
> it isn’t inconceivable that unitlessValue * valuelessUnit * valuelessUnit = unitlessValue * valuelessUnit.
It is. For example, what would be the meaning of this:
struct Foo {
// ...
}
2 * Foo
Valueless unit (or any type) is just not a thing, not in math, not in any programming language.
> But you seem to be imposing “mathematical expression” on an expression space where that’s already not the case? Whatever you may think of operator overloading, it is a thing that exists and it is a thing that “makes sense” to people using it idiomatically.
Operator overloading works on typed values, not "valueless" types. In some programming languages (like Python), class are values too, but why implement `a * MyClass` when you can write `MyClass(a)` which is 100% clearer on the intent?
Using operator overloading for types to implement casting is just black magic.
> expressions which look like maths don’t necessarily have a corresponding mathematical representation
Programming languages and the whole field of Computer Science is a branch of mathematics. They are not a natural language like english or german. They are an extension of maths.
> An equals infix operator in maths is a statement, establishing an immutable fact.
An operator only has meaning within the theory you use it.
For example:
`Matrix_A * Matrix_B` is not the same `*` as `Number_A * Number_B`
`1 + 2` is not the same `+` as `1 + 2 + 3 + ...`
`a = 3` in a math theorem is ont the same `=` as `a = 3` in a programming language (and that depends on the programming language)
As long as the theory defines the operators and the rules on how to use them, it does not matter which symbol you use. I can write a language where you have `<-` instead of `=`, and the mathematical rules (precedence, associativity, commutativity, ...) will be the same.
> Reacting as if it’s impossibly absurd that someone might read 1 * time.Seconds * time.Seconds as anything other than 1 * 1s * 1s is just snobbery.
First, that's not what I said. You should read that as `scalar * constant * constant` because reading that as `scalar * unit * unit` does not make sense nor in math, nor in any programming language.
If caring about readability and consistency is snobbery, then so be it.
> Not knowing Go, I focused on the syntax and the explicit values, and tried to build a syntax tree on top of it.
And the syntax is pretty explicit, because it's the same as math or any programming language: `scalar * constant * constant`. This is why using math as a point of reference is useful, you can easily make sense of what you're reading, no matter the syntax.
> I am a fan of lisps, so my mental syntax model was (* (* 1 time.Seconds) time.Seconds))
I still read this as `(* (* scalar constant) constant))`. And I expect your compiler/interpreter to throw an error if `time.Seconds` is anything without a clear value to evaluate the expression properly.
And I would expect to read `(* (* 1 (seconds 1) (seconds 1)))` as `scalar * quantity * quantity`, and I would expect to get square seconds as an output.
Anything else would not be correct and have little to no use.
You can just do `1 * time.Second + 2 * time.Minute` to do that. Adding times works intuitively. It's multiplying durations that gives you accelerations.