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

add dunder methods #6

wants to merge 2 commits into from

Conversation

andrewgsavage
Copy link
Collaborator

Do we want to add __divmod__, __round__, __floor__, and __trunc__?

Copy link

@nstarman nstarman left a comment

Choose a reason for hiding this comment

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

Q about the type hints.

Co-authored-by: Nathaniel Starkman <nstarman@users.noreply.github.com>
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]

@jorenham
Copy link

jorenham commented Mar 10, 2025

As much as I'd like it to be, typing.Self isn't a generic type 😅.

Maybe it's a good idea to add a type-checker to the CI?


edit:
it's pretty easy to work around luckily: just replace Self[#] with Quantity[#], rewrite your commit history, microwave your laptop, dye your hair, and nobody will notice 👌🏻.

@lucascolley
Copy link
Member

As much as I'd like it to be, typing.Self isn't a generic type 😅.

Maybe it's a good idea to add a type-checker to the CI?

Indeed, I just hit this locally! Yeah, we should set up CI at some point.

@jorenham
Copy link

Do you already have some verification code or something in place, that checks if assigning some real-world quantity object to the Quantity protocol is allowed by type-checkers?

@lucascolley
Copy link
Member

just replace Self[#] with Quantity[#]

It seemed like I needed to add quotation marks to appease Pylance locally doing this due to errors about Quantity being undefined. Pretty sure this was in a Python 3.13 env. Should it work without them?

@jorenham
Copy link

jorenham commented Mar 11, 2025

just replace Self[#] with Quantity[#]

It seemed like I needed to add quotation marks to appease Pylance locally doing this due to errors about Quantity being undefined. Pretty sure this was in a Python 3.13 env. Should it work without them?

Strinfying it is indeed a solution. Another option would be to from __future__ import annotations, which effectively stringifies all annotations automatically.

Should it work without them?

So no, not yet; that'll have to wait until 3.14: https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-pep649

@lucascolley
Copy link
Member

Do you already have some verification code or something in place, that checks if assigning some real-world quantity object to the Quantity protocol is allowed by type-checkers?

Not yet AFAIK, but certainly on the TO-DO list.

@jorenham
Copy link

Do you already have some verification code or something in place, that checks if assigning some real-world quantity object to the Quantity protocol is allowed by type-checkers?

Not yet AFAIK, but certainly on the TO-DO list.

My advice would then be to postpone this PR until you have it. Protocols are very tricky, and might not always work in the way you expect (especially when there are overloads involved).
While developing the numpy protocols in optype there have been a couple of times where I had to throw out days of work, because I only started testing after I wrote the protocols. Another painfull example of this mistake is https://github.com/jorenham/optype/commits/array-api/, which was another big waste of time.

Either way, don't repeat the same mistakes as I did. And even if you might learn some wise lessons by doing so, there are probably more efficient ways to achieve the goal 🤷🏻

@lucascolley
Copy link
Member

@andrewgsavage would you mind transferring this PR's changes over to https://github.com/quantity-dev/metrology-apis ? (no rush :) )

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants