Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

add dunder methods #6

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/quantity_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,26 @@ def value(self) -> V: ...
def unit(self) -> U: ...

### Dunder Methods

def __eq__[B](self, other: Self[op.CanEq[V, B], U], /) -> B: ...
def __ne__[B](self, other: Self[op.CanNe[V, B], U], /) -> B: ...
def __lt__[B](self, other: Self[op.CanLt[V, B], U], /) -> B: ...
def __le__[B](self, other: Self[op.CanLe[V, B], U], /) -> B: ...
def __gt__[B](self, other: Self[op.CanGt[V, B], U], /) -> B: ...
def __ge__[B](self, other: Self[op.CanGe[V, B], U], /) -> B: ...

def __pos__(self) -> Self[op.CanPos[V], U]: ...
def __neg__(self) -> Self[op.CanNeg[V], U]: ...
def __abs__(self) -> Self[op.CanAbs[V], U]: ...

def __add__[B](self, other: Self[op.CanAdd[V, B], U], /) -> Self[op.CanAdd[V, B], U]: ...
def __radd__[B](self, other: Self[op.CanAdd[V, B], U], /) -> Self[op.CanAdd[V, B], U]: ...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorenham is this the right way to think about the reflected operators? Just want a second for this motion 😆

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only situation where __radd__ would be called at runtime (apart from calling it directly), is if the right-hand-side is a strict subclass of Quantity (it's a weird exception). So that way, we can "reflect it back" to the __add__ of the left-hand-side, which tells us that the other in __add__ should match on op.CanRAdd instead of op.CanAdd. It'll be more symmetrical that way, so it's also prettier that way :P

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the casual higher-kinded typing notation using Self deserves full marks for creativity 💯

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm now that I think about this a bit more, I can imagine that type-checkers could end up in an infinite loop when analyzing this, just like my brain currently is, judging by the temperature of my office.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I only just now noticed: op.CanAdd[V, B] describes a type with a __add__: (self, other: V, /) -> B. So the return type should instead be Self[B, U]

def __sub__[B](self, other: Self[op.CanSub[V, B], U], /) -> Self[op.CanSub[V, B], U]: ...
def __rsub__[B](self, other: Self[op.CanSub[V, B], U], /) -> Self[op.CanSub[V, B], U]: ...

def __mul__[B](self, other: Self[op.CanMul[V, B], U], /) -> Self[op.CanMul[V, B], U]: ...
def __rmul__[B](self, other: Self[op.CanMul[V, B], U], /) -> Self[op.CanMul[V, B], U]: ...
def __truediv__[B](self, other: Self[op.CanDiv[V, B], U], /) -> Self[op.CanDiv[V, B], U]: ...
def __rtruediv__[B](self, other: Self[op.CanDiv[V, B], U], /) -> Self[op.CanDiv[V, B], U]: ...

def __pow__(self, other: int | Self[op.CanPow[V], U], /) -> Self[op.CanPow[V], U]: ...
def __rpow__(self, other: int | Self[op.CanPow[V], U], /) -> Self[op.CanPow[V], U]: ...