Div protocol signature is.. wrong?
protocol Div[A]:
@staticmethod
__truediv__ : (Self,Self) -> A
@staticmethod
__itruediv__ : (Self,Self) -> A
I have a Duration class that represents a duration. It has a value for seconds and one for fractions of a second. I want to use it to compute an average, so I first add up multiple Duration and then I want to divide by an integer but I think this protocol only allows dividing by the same time. That's got to be wrong, right? Wouldn't the normal thing be to divide by an integer?
@nordlander plxz advice :)
So far we've imagined that the division operator must be overloaded to support at least the following concrete instances:
(float, float) -> float
(complex, complex) -> complex
(int, int) -> float
It's the last instance that forces us to introduce an additional type parameter in the Div protocol:
protocol Div[A]:
@static
__truediv__ : (Self, Self) -> A
Now we have a new example, where we want to support the instance
(Duration, int) -> Duration
This doesn't fit the pattern encoded in Div above, so perhaps we must consider adding another parameter to the Div protocol:
protocol Div[A,B]:
@static
__truediv__ : (Self, A) -> B
I haven't pondered this very long, but it seems it wouldn't lead to any ambiguities (our protocol parameters always always imply a functional dependency on Self, and the division instance would still be unambiguously determined by the left operand type).
Let's think a bit more and return to this topic soon!
BTW, note that the type
(int, int) -> int
isn't part of the Div instances defined in __builtin__, because that form of division is expressed using the // (__floordiv__ ) operator in Python/Acton (which is a member of the Integral protocol).
I agree with what Johan writes, but offer some further comments, which may be too philosophical, and an alternative proposal.
Implementations of the protocol Div are, I believe, intended to provide what is ideally "true division". Of course, without arbitrary precision reals we have to stick to floating point numbers and "approximately true division".
But if your Duration type has integer fractions of seconds (e.g. nanoseconds) and you want to divide it by an integer to provide a fractional Duration, then I feel that you are morally doing integer division, so for a Duration d one would want to write d//10 to get the Duration which is (approximately) one tenth of d, and d%10 get the remainder, i.e. the Duration d - (d//10) * 10.
Note that the Times protocol is already generalized with a type parameter to let Duration implement Times[int], so that the multiplication above is OK (this was mainly introduced to allow for examples like ls * 100 where ls is a list).
One can think of many types that would have use for division by an integer to give a smaller piece of the same type, and perhaps also a mod operator. This suggests
protocol IDiv[A]:
@staticmethod
__divmod__ : (Self,A) -> (Self,Self)
@staticmethod
__floordiv__ : (Self,A) -> Self
@staticmethod
__mod__ : (Self,A) -> Self
and then letting Integral be a subprotocol of IDiv[Self].
Duration would implement Plus, Times[int] and IDiv[int] (or with i64,... for type parameter).
That's a good argument! And IDiv makes much sense, not the least as a kind of dual to Times.
My only additional thought is what one would do if Duration happened to be defined using a float ("approximative real") instead. Then dividing a duration into N equal parts would appear more like a "true" division, even when N is an integer. Still, converting N into a Duration just to be able to use the Div protocol wouldn't look right at all.
Perhaps one just has to accept that our floordiv is a slightly more general concept than the name suggests?