acton icon indicating copy to clipboard operation
acton copied to clipboard

Div protocol signature is.. wrong?

Open plajjan opened this issue 2 years ago • 3 comments

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 :)

plajjan avatar Sep 29 '23 20:09 plajjan

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).

nordlander avatar Oct 02 '23 09:10 nordlander

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).

sydow avatar Oct 02 '23 12:10 sydow

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?

nordlander avatar Oct 02 '23 13:10 nordlander