-
-
Notifications
You must be signed in to change notification settings - Fork 31
Balancing correctness vs. type sanity #12
Comments
What about |
IMO Option 2 is kind of the worst of both worlds -- if a function returns a class ndarray:
def all(self) -> bool:
return True
def sin(x: ndarray) -> Union[int, ndarray]:
return x
sin(ndarray()).all() Option 1 immediately loses all typechecking without type assertions everywhere -- even simple The downside to option 3 is undesirable that you have to change x = np.array(3)
np.sin(x) to from typing import cast
np.sin(float(x))
# or
np.sin(cast(float, x))
# or
cast(np.float32, np.sin(x)) but I think this happens rarely enough that typechecking large codebases would still be worth that effort. |
@njsmith I'm not sure that'd work in this case. (np.sin(x) > 5).all() should (ideally) type-check with if This actually brings up another quirk though -- things like: np.sin([1])
np.sin([[1]]
np.sin([[1, 2], (3, 4)])
np.sin([[[[[[1]]]]]]) are all legal, but I'm not sure how to express that duckarray = Union[
ndarray,
Sequence[T],
Sequence[duckarray], # for nested sequences
] but MyPy doesn't support recursive types and I'm not sure there's a good way to fully express the types otherwise. |
|
Indeed,
That said, I'm sure the necessity to type cast would still be annoying. |
Oof -- that's embarrassing 🙊. For some reason I thought that If |
Closing this-we've been making correctness versus sanity judgement calls on the go as the types evolve, probably the important thing now is to make sure we document the decisions. (For now they tend to be documented with comments in tests cases; that needs to be improved.) |
#11 surfaced an important issue to decide: should our type annotations prioritize correctness or type sanity/friendliness.
I wrote:
@alanhdu responded:
Here's an illustrative example. NumPy ufuncs like
np.sin()
usually return a numpy.ndarray when passed a numpy.ndarray as input. However, there are two notable exceptions:__array_ufunc__
(or other old override mechanisms) to return whatever they like.In practice, both 0-dimensional arrays and ndarray subclasses (especially those that would violate this design) are rare compared to the dominant usecase, but these cases definitely do come up in large code-bases.
Neither of these exceptions are currently expressible with our typing system, so we can choose between:
def sin(x: np.ndarray) -> Any
(correct, but useless for type checking)def sin(x: np.ndarray) -> Union[np.generic, np.ndarray]
(not worrying about ndarray subclasses -- you shouldn't write a subclass that violates NumPy's typing rules)def sin(x: np.ndarray) -> np.ndarray
(not worrying about subclasses or 0-dimensional arrays)(Note that this would be one of several overrides for ufuncs, which also should be defined for non-ndarrays.)
The text was updated successfully, but these errors were encountered: