Why isn’t DurationProtocol.*(_:_:) commutative?

i had written this earlier today:

let days:Int = 7
return now.advanced(by: days * .seconds(86400))

it didn’t compile, which was puzzling for a while, until i tried swapping the order of the operands to

let days:Int = 7
return now.advanced(by: .seconds(86400) * days)

at first i suspected a typechecker bug, but then it dawned on me: DurationProtocol.*(_:_:) is not commutative! this was unexpected, since * is supposed to represent multiplication here, and multiplication ought to be commutative.

why isn’t DurationProtocol.*(_:_:) commutative?


I had brought this up during review, but it wasn't addressed, so :man_shrugging:
There doesn't appear to be any difficulty in principle, just an oversight:


The limited usage of * was reduced due to unexpected compile time increases in completely un-related code (which is a known issue). That overload may have just been removed too zealously. If possible, per tests of compile times, I would consider it to be a bug and we could likely add it without much of an issue.


ironically, i’m coming to think that if we were limited to one overload, then (Self, Int) -> Self was the wrong choice, because it prevents us from writing something like

let t:Duration = 7 * .day

instead it has to be written like

let t:Duration = .day * 7

which just sounds wrong in english.

definitely too late to change, but it locks us into the .days(7) static function pattern, which i’ve found doesn’t compose quite as well as the unit notation.

1 Like

I just hit this organically. The compiler error message was pretty straightforward (other than a weirdly bogus fix-it) so it didn't cause much delay, but it was surprising.

I agree that the mandated order is the exact opposite of what's intuitive to write (and to read).

1 Like