Skip to content

Commit 2da540e

Browse files
committed
clear up formatting of discussion
1 parent a42bf48 commit 2da540e

File tree

1 file changed

+14
-11
lines changed

1 file changed

+14
-11
lines changed

docs/jep/12049-type-annotations.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Again, there are a couple mechanisms that could be used for this:
239239
A decision we need to make is whether `ArrayAnnotation` and `ArrayInstance` should be the same or different objects. There is some precedent here; for example in the core Python language spec, `typing.Dict` and `typing.List` exist for the sake of annotation, while the built-in `dict` and `list` serve the purposes of instance checks.
240240
However, `Dict` and `List` are [deprecated](https://peps.python.org/pep-0585/#implementation) in newer Python versions in favor of using `dict` and `list` for both annotation and instance checks.
241241

242-
#### Following NumPy's lead
242+
### Following NumPy's lead
243243

244244
In NumPy's case, `np.typing.NDArray` serves the purpose of type annotations, while `np.ndarray` serves the purpose of instance checks (as well as array type identity).
245245
Given this, it may be reasonable to conform to NumPy's precedent and implement the following:
@@ -250,11 +250,11 @@ Given this, it may be reasonable to conform to NumPy's precedent and implement t
250250

251251
This might feel somewhat natural to NumPy power-users, however this trifurcation would likely be a source of confusion: the choice of which to use for instance checks and annotations is not immediately clear.
252252

253-
#### Unification
253+
### Unifying instance checks and annotation
254254

255255
Another approach would be to unify type checking and annotation via override mechanisms mentioned above.
256256

257-
##### Option 1: Partial unification
257+
### Option 1: Partial unification
258258
A partial unification might look like this:
259259

260260
- `jax.Array` is the actual type of on-device arrays.
@@ -264,7 +264,7 @@ A partial unification might look like this:
264264
In this approach, `jax.numpy.ndarray` would become a simple alias `jax.typing.Array` for backward compatibility.
265265

266266

267-
##### Option 2: Full unification via overrides
267+
#### Option 2: Full unification via overrides
268268
Alternatively, we could opt for full unification via overrides:
269269

270270
- `jax.Array` is the actual type of on-device arrays.
@@ -273,7 +273,7 @@ Alternatively, we could opt for full unification via overrides:
273273

274274
Here, `jax.numpy.ndarray` would become a simple alias `jax.Array` for backward compatibility.
275275

276-
##### Option 3: Full unification via class hierarchy
276+
#### Option 3: Full unification via class hierarchy
277277
Finally, we could opt for full unification via restructuring of the class hierarchy and replacing duck-typing with OOP object hierarchies:
278278

279279
- `jax.Array` is the actual type of on-device arrays
@@ -283,7 +283,7 @@ Finally, we could opt for full unification via restructuring of the class hierar
283283
Here `jnp.ndarray` could be an alias for `jax.Array`.
284284
This final approach is in some senses the most pure, but it may be challenging from an OOP design standpoint (`Tracer` *is a* `Array`?).
285285

286-
##### Option 4: Parial unification via class hierarchy
286+
#### Option 4: Parial unification via class hierarchy
287287
We could appease OOP pedants by instead making `Tracer` and `Array` derive from a common `ArrayBase` base class:
288288

289289
- `jax.Array` is the actual type of on-device arrays
@@ -293,12 +293,15 @@ We could appease OOP pedants by instead making `Tracer` and `Array` derive from
293293
Here `jnp.ndarray` would be an alias for `ArrayBase`.
294294
This may be purer from an OOP perspective, but it reintroduces a bifurcation and the distinction between `Array` and `ArrayBase` for annotation and instance checks may become confusing.
295295

296-
##### Evaluation
296+
#### Evaluation
297297

298-
There is no perfect option here, but from the point of view of the user, Options 2 and 3 arguably present the most clear user-facing API: `jax.Array` is all you need to know.
299-
Option 3 (`Tracer` as a subclass of `Array`) is perhaps the purer approach, but on the other hand it would require `Tracer` objects to carry all the baggage of `Array` objects (data buffers, sharding, devices, etc.) For this reason, I (@jakevdp) believe that Option 2 presents the best path forward.
300-
It offers the least confusing API for users and does not require any significant restructuring of our existing codepaths.
301-
There is one minor technical hurdle involved; that is that `jax.Array` will be defined in C++ via pybind11, and pybind11 currently [does not support](https://github.com/pybind/pybind11/issues/2696) custom metaclasses required for overriding `__instancecheck__`; but I'd consider this a technical hurdle rather than a blocker.
298+
Considering the overall strengths and weaknesses of each potential approach:
299+
300+
- From a user perspective, the unified approaches (options 2 and 3) are arguably best, because they remove the cognitive overhead involved in remembering which objects to use for instance checks or annotations: `jax.Array` is all you need to know
301+
- Between Option 2 and Option 3, the purer (in an OOP sense) apporach is arguably Option 3 (`Tracer` as a subclass of `Array`), but in other senses it breaks the inheritance model, because it would require `Tracer` objects to carry all the baggage of `Array` objects (data buffers, sharding, devices, etc.)
302+
- Option 2 is less pure in an OOP sense, but it aligns more closely with the spirit of how JAX is designed, with `Tracer` objects duck-typing as `Arrays`, and using mechanisms that Python provides to support this kind of duck typing. There is one minor technical hurdle involved; that is that `jax.Array` will be defined in C++ via pybind11, and pybind11 currently [does not support](https://github.com/pybind/pybind11/issues/2696) custom metaclasses required for overriding `__instancecheck__`; but I'd consider this a technical hurdle rather than a blocker.
303+
304+
With this in mind, we conclude that Option 2 presents the best path forward.
302305

303306
### Implementation Plan
304307

0 commit comments

Comments
 (0)