Skip to content

Commit

Permalink
Add challenge 5
Browse files Browse the repository at this point in the history
  • Loading branch information
jakevdp committed Aug 12, 2022
1 parent e585bf3 commit 56ff700
Showing 1 changed file with 27 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/jep/11859-type-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,30 @@ Python has a plan to enable type annotations with this level of granularity via

There are some third-party projects that may help in the meantime, in particular [google/jaxtyping](https://github.com/google/jaxtyping), but this uses non-standard annotations and may not be suitable for annotating JAX itself.
All told, the array-type-granularity challenge is less of an issue than the other challenges, because the main effect is that array-like annotations will be less specific than they otherwise could be.

## Challenge 5: Imprecise APIs inherited from NumPy
A large part of JAX’s user-facing API is inherited from NumPy within the `jax.numpy` submodule.
NumPy’s API was developed years before static type checking became part of the Python language, and follows Python’s historic recommendations to use a [duck-typing](https://docs.python.org/3/glossary.html#term-duck-typing)/[EAFP](https://docs.python.org/3/glossary.html#term-eafp) coding style, in which strict type-checking at runtime is discouraged. As a concrete example of this, consider the `numpy.tile` function, which is defined like this:
```python
def tile(A, reps):
try:
tup = tuple(reps)
except TypeError:
tup = (reps,)
d = len(tup)
...
```
Here the *intent* is that `reps` would contain either an `int` or a sequence of `int` values, but the *implementation* allows `tup` to be any iterable.
When adding annotations to this kind of duck-typed code, we could take one of two routes:
1. We may choose to annotate the *intent* of the function's API, which here might be something like `reps: Union[int, Sequence[int]]`.
2. Conversely, we may choose to annotate the *implementation* of the function, which here might look something like `reps[Union[ConvertibleToInt, Iterable[ConvertibleToInt]]` where `ConvertibleToInt` is a special protocol that covers the exact mechanism by which our function converts the inputs to integers (i.e. via `__int__`, via `__index__`, via `__array__`, etc.). Note also here that in a strict sense, `Iterable` is not sufficient here because there are objects in Python that duck-type as iterables but do not satisfy a static type check against `Iterable` (namely, an object that is iterable via `__getitem__` rather than `__iter__`.)

The advantage of #1, annotating intent, is that the annotations are more useful to the user in communicating the API contract; while for the developer the flexibility leaves room for refactoring when necessary. The down-side (particularly for gradually-typed APIs like JAX's) is that it's quite likely that user code exists which runs correctly, but would be flagged as incorrect by a type checker.
Gradual typing of an existing duck-typed API means that the current annotation is implicitly `Any`, so changing this to a stricter type may present to users as a breaking change.

Broadly speaking, annotating intent better serves Level 1 type checking, while annotating implementation better serves Level 3, while Level 2 is more of a mixed bag (both intent and implementation are important when it comes to annotations in IDEs).

## JAX Type Annotation Roadmap
With this framing (Level 1/2/3) and JAX-specific challenges in mind, we can begin to develop our roadmap for implementing consistent type annotations across the JAX project.

*TODO(jakevdp).... finish this discussion*

0 comments on commit 56ff700

Please sign in to comment.