> Incorrect. You must cast the user value to a time.Duration, since Go is a type-safe language, and like most type-safe [1] languages, its numerical operations generally require their operands to be of the same type.
This is cargo-cult type-safety; stop a moment to consider the dimensions implied by the types.
To be clear: time.Duration is a type, and is the only part of this discussion where “type safety” is a factor.
time.Millisecond is a constant time.Duration whose value represents the duration of a millisecond. time.Millisecond et al are not a unit as in chemistry class, so you’re not supposed to get a time.Millisecond^2 by multiplying them. Just like in any other typed language, T * T -> T, not T^2 (where T = time.Duration), since it’s a type and not a unit.
time.Millisecond et al instead make you explicitly annotate the scale of user-provided durations. When you write
d := time.Second*time.Duration(input)
you are assigning to d a value of type time.Duration equal to the duration of one second multiplied by the input. You are free to not use the constants defined by the time package and instead create time.Durations from the literal number of nanoseconds in the duration, if you so choose:
d := time.Duration(input)*1000000000
But since the time package already defines convenient constants for standard durations, those will be used instead and be far more explicit and readable.
This has nothing to do with dimensional analysis of units, of the sort you do in physical sciences. I don’t know of any general-purpose languages that include dimensional analysis in the language or their standard library’s datetime package, but I’d be curious if you know of one (that isn’t specifically geared towards the sciences) - seems like the sort of thing Ada might include? In any case, I don’t think a language focused on keeping a simple feature set like Go should be a trailblazer here. Durations are easy.
It's still bonkers to make you cast an non-duration input to time.Duration. this causes cognitive confusion, because you are effectively labelling the multiplicand as something that it isn't. All pls with types have semantic meaning for the types if nothing else, so if you can't see why this is a real problem, I can only say: Stockholm syndrome.
"numerical operations generally require their operands to be of the same type"
The correct decision would have been to make the * operator be allowed to operate on a time.Duration and an integer, just as you are uncontroversially allowed to operate * on a float and an integer -- to refute your statement and go even stronger I don't know of ANY pls that require * operands be the same type.
However, that is not what go chose. And we are talking about the choices go made. This is very much NOT well thought out and very ill-considered, especially since "the right thing" is so easy.
> I don't know of ANY pls that require * operands be the same type.
Haskell is an example, where the type of * is:
(*) :: Num a => a -> a -> a
Which says that the arguments must be numeric, and of the same type. I think this is the sensible choice from a strongly-typed perspective, and some operation which allows one to multiply a time value should be a separate thing.
But you can define your own * operator, separate to the one from Num, with any types you like, can't you? You might have to hide the one from the prelude to use it.
> to refute your statement and go even stronger I don't know of ANY pls that require * operands be the same type.
This really comes down to a lack of experience on your part. Haskell requires both arguments to be of the same type and only in the case where it can reasonably infer from a literal that it could be coerced will it do so. OCaml, as another example, requires an entirely different multiplication operator for floats.
Requiring the same type for both arguments is not as rare a position as you've made it out to be, and not a showstopper in any case either.
> this causes cognitive confusion, because you are effectively labelling the multiplicand as something that it isn't
You make a fair point, but at that point the debate is about other choices made in the Go language disallowing implicit type conversions.
> The correct decision would have been to make the * operator be allowed to operate on a time.Duration and an integer, just as you are uncontroversially allowed to operate * on a float and an integer
What should the resulting type of the multiplication operator be when applied to a float and an integer? Should the type be different if the operands are swapped? Is it acceptable for the multiplication operator to not be commutative, given that we seem to be demanding a great deal of rigor from our type system? Should we only allow this implicit conversion if type inference is not being used?
> To refute your statement and go even stronger I don't know of ANY pls that require * operands be the same type
This is cargo-cult type-safety; stop a moment to consider the dimensions implied by the types.