Skip to content

Commit 0dee08c

Browse files
committed
Lots more tweaks
1 parent 93f91ab commit 0dee08c

1 file changed

Lines changed: 87 additions & 43 deletions

File tree

pep.rst

Lines changed: 87 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,12 @@ Object creation
673673
specified by ``Member`` arguments
674674

675675
* ``NewProtocolWithBases[Bases: tuple[type], *Ms: Member]`` - A variant that
676-
allows specifying bases too. TODO: Is this something we actually want?
676+
allows specifying bases too. The idea is that a type would satisfy
677+
this protocol if it extends all of the given bases and has the
678+
specified members. (TODO: Is this something we actually
679+
want? It would would be a potentially powerful feature for dealing
680+
with things like Pydantic models, but protocol-with-bases would be
681+
something of a new concept.)
677682

678683
* ``NewTypedDict[*Ps: Member]`` - Creates a new ``TypedDict`` with
679684
items specified by the ``Member`` arguments. TODO: Do we want a way
@@ -886,13 +891,14 @@ Runtime evaluation support
886891
--------------------------
887892

888893
An important goal is supporting runtime evaluation of these computed
889-
types. We do not propose to add an official evaluator to the standard
894+
types. We **do not** propose to add an official evaluator to the standard
890895
library, but intend to release a third-party evaluator library.
891896

892897
While most of the extensions to the type system are "inert" type
893-
operator applications, the syntax also includes list iteration and
894-
conditionals, which will be automatically evaluated when the
895-
``__annotate__`` method of a class, alias, or function is called.
898+
operator applications, the syntax also includes list iteration,
899+
conditionals, and attribute access, which will be automatically
900+
evaluated when the ``__annotate__`` method of a class, alias, or
901+
function is called.
896902

897903
In order to allow an evaluator library to trigger type evaluation in
898904
those cases, we add a new hook to ``typing``:
@@ -1149,6 +1155,10 @@ I am proposing a fully new extended callable syntax because:
11491155
closely mimic the ``mypy_extensions`` version though, if something new
11501156
is a non starter)
11511157

1158+
TODO: Currently I made the qualifiers be short strings, for code brevity
1159+
when using them, but an alternate approach would be to mirror
1160+
``inspect.Signature`` more directly, and have an enum with names like
1161+
``ParamKind.POSITIONAL_OR_KEYWORD``.
11521162

11531163
.. _generic-callable-rationale:
11541164

@@ -1226,6 +1236,9 @@ like mapped types are unmentioned in current documentation
12261236
Reference Implementation
12271237
========================
12281238

1239+
There is a demo of a runtime evaluator [#runtime]_, which is
1240+
also where this PEP draft currently lives.
1241+
12291242
There is an in-progress proof-of-concept implementation in mypy [#ref-impl]_.
12301243

12311244
It can type check the ORM and FastAPI-style model derivation
@@ -1234,8 +1247,6 @@ examples.
12341247
It is missing support for callables, ``UpdateClass``, annotation
12351248
processing, and various smaller things.
12361249

1237-
There is a demo of a runtime evaluator as well [#runtime]_.
1238-
12391250
Alternate syntax ideas
12401251
======================
12411252

@@ -1374,15 +1385,48 @@ Rejected Ideas
13741385
Renounce all cares of runtime evaluation
13751386
----------------------------------------
13761387

1377-
This would have a lot of simplifying features.
1388+
This would give us more flexibility to experiment with syntactic
1389+
forms, and would allow us to dispense with some ugliness such as
1390+
requiring ``typing.Iter`` in unpacked comprehension types and having a
1391+
limited set of ``<type-bool>`` expressions that can appear in
1392+
conditional types.
13781393

1379-
TODO: Expand
1394+
For better or worse, though, runtime use of type annotations is
1395+
widespread, and one of our motivating examples (automatically deriving
1396+
FastAPI CRUD models) depends on it.
13801397

13811398
Support TypeScript style pattern matching in subtype checking
13821399
-------------------------------------------------------------
13831400

1384-
This would almost certainly only be possible if we also decide not to
1385-
care about runtime evaluation, as above.
1401+
In TypeScript, conditional types are formed like::
1402+
1403+
SomeType extends OtherType ? TrueType : FalseType
1404+
1405+
What's more, the right hand side of the check allows binding type
1406+
variables based on pattern matching, using the ``infer`` keyword, like
1407+
this example that extracts the element type of an array::
1408+
1409+
type ArrayArg<T> = T extends [infer El] ? El : never;
1410+
1411+
This is a very elegant mechanism, especially in the way that it
1412+
eliminates the need for ``typing.GetArg`` and its subtle ``Base``
1413+
parameter.
1414+
1415+
Unfortunately it seems very difficult to shoehorn into Python's
1416+
existing syntax in any sort of satisfactory way, especially because of
1417+
the subtle binding structure.
1418+
1419+
Perhaps the most plausible variant would be something like::
1420+
1421+
type ArrayArg[T] = El if IsAssignable[T, list[Infer[El]]] else Never
1422+
1423+
Then, if we wanted to evaluate it at runtime, we'd need to do
1424+
something gnarly involving a custom ``globals`` environment that
1425+
catches the unbound ``Infer`` arguments.
1426+
1427+
Additionally, without major syntactic changes (using type operators
1428+
instead of ternary), we wouldn't be able to match TypeScript's
1429+
behavior of lifting the conditional over unions.
13861430

13871431

13881432
Replace ``IsAssignable`` with something weaker than "assignable to" checking
@@ -1410,6 +1454,34 @@ that is similar to but not the same as subtyping, and that would need
14101454
to either have a long and weird name like ``IsAssignableSimilar`` or a
14111455
misleading short one like ``IsAssignable``.
14121456

1457+
1458+
Don't use dot notation to access ``Member`` components
1459+
------------------------------------------------------
1460+
1461+
Earlier versions of this PEP draft omitted the ability to write
1462+
``m.name`` and similar on ``Member`` and ``Param`` components, and
1463+
instead relied on helper operators such as ``typing.GetName`` (that
1464+
could be implemented under the hood using ``typing.GetArg`` or
1465+
``typing.GetMemberType``).
1466+
1467+
The potential advantage here is reducing the number of new constructs
1468+
being added to the type language, and avoiding needing to either
1469+
introduce a new general mechanism for associated types or having a
1470+
special-case for ``Member``.
1471+
1472+
``PropsOnly`` (from :ref:`the query builder example <qb-impl>`) would
1473+
look like::
1474+
1475+
type PropsOnly[T] = typing.NewProtocol[
1476+
*[
1477+
typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1478+
for p in typing.Iter[typing.Attrs[T]]
1479+
if typing.IsAssignable[typing.GetType[p], Property]
1480+
]
1481+
]
1482+
1483+
Everyone hated how this looked a lot.
1484+
14131485
.. _less_syntax:
14141486

14151487

@@ -1430,8 +1502,9 @@ Boolean operations would likewise become operators (``Not``, ``And``,
14301502
etc).
14311503

14321504
The advantage of this is that constructing a type annotation never
1433-
needs to do non-trivial computation, and thus we don't need
1434-
:ref:`runtime hooks <rt-support>` to support evaluating them.
1505+
needs to do non-trivial computation (assuming we also get rid of dot
1506+
notation), and thus we don't need :ref:`runtime hooks <rt-support>` to
1507+
support evaluating them.
14351508

14361509
It would also mean that it would be much easier to extract the raw
14371510
type annotation. (The lambda form would still be somewhat fiddly.
@@ -1448,35 +1521,6 @@ worse. Supporting filtering while mapping would make it even more bad
14481521

14491522
We can explore other options too if needed.
14501523

1451-
1452-
Don't use dot notation to access ``Member`` components
1453-
------------------------------------------------------
1454-
1455-
Earlier versions of this PEP draft omitted the ability to write
1456-
``m.name`` and similar on ``Member`` and ``Param`` components, and
1457-
instead relied on helper operators such as ``typing.GetName`` (that
1458-
could be implemented under the hood using ``typing.GetArg`` or
1459-
``typing.GetMemberType``).
1460-
1461-
The potential advantage here is reducing the number of new constructs
1462-
being added to the type language, and avoiding needing to either
1463-
introduce a new general mechanism for associated types or having a
1464-
special-case for ``Member``.
1465-
1466-
``PropsOnly`` (from :ref:`the query builder example <qb-impl>`) would
1467-
look like::
1468-
1469-
type PropsOnly[T] = typing.NewProtocol[
1470-
*[
1471-
typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1472-
for p in typing.Iter[typing.Attrs[T]]
1473-
if typing.IsAssignable[typing.GetType[p], Property]
1474-
]
1475-
]
1476-
1477-
Everyone hated how this looked a lot.
1478-
1479-
14801524
Perform type manipulations with normal Python functions
14811525
-------------------------------------------------------
14821526

@@ -1559,7 +1603,7 @@ arguments invariantly.
15591603
Acknowledgements
15601604
================
15611605

1562-
Jukka Lehtosalo
1606+
Jukka Lehtosalo, etc
15631607

15641608
[Thank anyone who has helped with the PEP.]
15651609

0 commit comments

Comments
 (0)