Hacker News new | ask | show | jobs
by cytzol 1554 days ago
Like many things with Go, its approach seems reasonable and simple at first, but allows you to accidentally write code that looks right but is very, very wrong. For example, what do you think this code will do?

    delaySecs := 1 * time.Second
    time.Sleep(delaySecs * time.Second)
Now I insist on using the durationcheck lint to guard against this (https://github.com/charithe/durationcheck). It found a flaw in some exponential-backoff code I had refactored but couldn’t easily fully test that looked right but was wrong, and now I don’t think Go’s approach is reasonable anymore.
2 comments

Perhaps the function shouldn't accept the unit of sec². Not least because I have no idea what a delay in that unit could signify.
It doesn't actually use units. Everything is in nanoseconds, so time.Second is just another unitless number.

  const (
   Nanosecond  Duration = 1
   Microsecond          = 1000 * Nanosecond
   Millisecond          = 1000 * Microsecond
   Second               = 1000 * Millisecond
   Minute               = 60 * Second
   Hour                 = 60 * Minute
  )
Note that the wonderful Go type system interprets time.Second * time.Second as 277777h46m40s with the type time.Second (not sec^2)

  time.Second * time.Second
The type of this is `time.Duration` (or int64 internally), not `time.Second` (which is a const with a value).

I agree, though, that this is not quite sound, because it can be misused, as shown above with `time.Sleep(delaySecs * time.Second)`.

In Kotlin you can do `1.seconds + 1.minutes` but not `1.seconds * 1.minutes` (compilation error), which I quite like. Here is a playground link: https://pl.kotl.in/YZLu97AY8

Certainly, but for that the type system should be rich enough to support unit designators.

I know how to implement that in Haskell, and that it can be implemented in C++ and Rust. I know how to logically implement that in Java or Typescript, but usability will suck (no infix operators).

Go tends to cover such things by incorporating them directly in the language. But then it tends to not cover them at all because it would "overcomplicate" the language...

For a good example of what it looks like when somebody does bother to do it, see F# units of measure.

This looks to me like the semantics are good but the implementation details are broken. 1 * time.Second * time.Second semantically reads to me as 1 second. If time.Second is some numeric value, that’s obviously wrong everwhere unless the type system reflects and enforces the unit conversion.
> 1 * time.Second * time.Second semantically reads to me as 1 second.

Which is wrong, 1s * 1s = 1s².

For example, the force of gravity is expressed in m/s² and describe an acceleration (m/s / s, aka a change of velocity per time units, where velocity is a change of distance per time units).

Okay so do I need to consult Relativity to program 1sec + 2min?
Since 1min could be 61 seconds[1], yes?

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.

  [1] - https://en.wikipedia.org/wiki/Leap_second
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.

What is the "scalar value of the unit"?

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???

[1] would that be unitful?

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.