Skip to content

Commit 1adb362

Browse files
committed
Lots more tweaks
1 parent b9e35a2 commit 1adb362

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
@@ -720,7 +720,12 @@ Object creation
720720
specified by ``Member`` arguments
721721

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

725730
* ``NewTypedDict[*Ps: Member]`` - Creates a new ``TypedDict`` with
726731
items specified by the ``Member`` arguments. TODO: Do we want a way
@@ -933,13 +938,14 @@ Runtime evaluation support
933938
--------------------------
934939

935940
An important goal is supporting runtime evaluation of these computed
936-
types. We do not propose to add an official evaluator to the standard
941+
types. We **do not** propose to add an official evaluator to the standard
937942
library, but intend to release a third-party evaluator library.
938943

939944
While most of the extensions to the type system are "inert" type
940-
operator applications, the syntax also includes list iteration and
941-
conditionals, which will be automatically evaluated when the
942-
``__annotate__`` method of a class, alias, or function is called.
945+
operator applications, the syntax also includes list iteration,
946+
conditionals, and attribute access, which will be automatically
947+
evaluated when the ``__annotate__`` method of a class, alias, or
948+
function is called.
943949

944950
In order to allow an evaluator library to trigger type evaluation in
945951
those cases, we add a new hook to ``typing``:
@@ -1236,6 +1242,10 @@ I am proposing a fully new extended callable syntax because:
12361242
closely mimic the ``mypy_extensions`` version though, if something new
12371243
is a non starter)
12381244

1245+
TODO: Currently I made the qualifiers be short strings, for code brevity
1246+
when using them, but an alternate approach would be to mirror
1247+
``inspect.Signature`` more directly, and have an enum with names like
1248+
``ParamKind.POSITIONAL_OR_KEYWORD``.
12391249

12401250
.. _generic-callable-rationale:
12411251

@@ -1313,6 +1323,9 @@ like mapped types are unmentioned in current documentation
13131323
Reference Implementation
13141324
========================
13151325

1326+
There is a demo of a runtime evaluator [#runtime]_, which is
1327+
also where this PEP draft currently lives.
1328+
13161329
There is an in-progress proof-of-concept implementation in mypy [#ref-impl]_.
13171330

13181331
It can type check the ORM, FastAPI-style model derivation, and
@@ -1321,8 +1334,6 @@ NumPy-style broadcasting examples.
13211334
It is missing support for callables, ``UpdateClass``, annotation
13221335
processing, and various smaller things.
13231336

1324-
There is a demo of a runtime evaluator as well [#runtime]_.
1325-
13261337
Alternate syntax ideas
13271338
======================
13281339

@@ -1461,15 +1472,48 @@ Rejected Ideas
14611472
Renounce all cares of runtime evaluation
14621473
----------------------------------------
14631474

1464-
This would have a lot of simplifying features.
1475+
This would give us more flexibility to experiment with syntactic
1476+
forms, and would allow us to dispense with some ugliness such as
1477+
requiring ``typing.Iter`` in unpacked comprehension types and having a
1478+
limited set of ``<type-bool>`` expressions that can appear in
1479+
conditional types.
14651480

1466-
TODO: Expand
1481+
For better or worse, though, runtime use of type annotations is
1482+
widespread, and one of our motivating examples (automatically deriving
1483+
FastAPI CRUD models) depends on it.
14671484

14681485
Support TypeScript style pattern matching in subtype checking
14691486
-------------------------------------------------------------
14701487

1471-
This would almost certainly only be possible if we also decide not to
1472-
care about runtime evaluation, as above.
1488+
In TypeScript, conditional types are formed like::
1489+
1490+
SomeType extends OtherType ? TrueType : FalseType
1491+
1492+
What's more, the right hand side of the check allows binding type
1493+
variables based on pattern matching, using the ``infer`` keyword, like
1494+
this example that extracts the element type of an array::
1495+
1496+
type ArrayArg<T> = T extends [infer El] ? El : never;
1497+
1498+
This is a very elegant mechanism, especially in the way that it
1499+
eliminates the need for ``typing.GetArg`` and its subtle ``Base``
1500+
parameter.
1501+
1502+
Unfortunately it seems very difficult to shoehorn into Python's
1503+
existing syntax in any sort of satisfactory way, especially because of
1504+
the subtle binding structure.
1505+
1506+
Perhaps the most plausible variant would be something like::
1507+
1508+
type ArrayArg[T] = El if IsAssignable[T, list[Infer[El]]] else Never
1509+
1510+
Then, if we wanted to evaluate it at runtime, we'd need to do
1511+
something gnarly involving a custom ``globals`` environment that
1512+
catches the unbound ``Infer`` arguments.
1513+
1514+
Additionally, without major syntactic changes (using type operators
1515+
instead of ternary), we wouldn't be able to match TypeScript's
1516+
behavior of lifting the conditional over unions.
14731517

14741518

14751519
Replace ``IsAssignable`` with something weaker than "assignable to" checking
@@ -1497,6 +1541,34 @@ that is similar to but not the same as subtyping, and that would need
14971541
to either have a long and weird name like ``IsAssignableSimilar`` or a
14981542
misleading short one like ``IsAssignable``.
14991543

1544+
1545+
Don't use dot notation to access ``Member`` components
1546+
------------------------------------------------------
1547+
1548+
Earlier versions of this PEP draft omitted the ability to write
1549+
``m.name`` and similar on ``Member`` and ``Param`` components, and
1550+
instead relied on helper operators such as ``typing.GetName`` (that
1551+
could be implemented under the hood using ``typing.GetArg`` or
1552+
``typing.GetMemberType``).
1553+
1554+
The potential advantage here is reducing the number of new constructs
1555+
being added to the type language, and avoiding needing to either
1556+
introduce a new general mechanism for associated types or having a
1557+
special-case for ``Member``.
1558+
1559+
``PropsOnly`` (from :ref:`the query builder example <qb-impl>`) would
1560+
look like::
1561+
1562+
type PropsOnly[T] = typing.NewProtocol[
1563+
*[
1564+
typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1565+
for p in typing.Iter[typing.Attrs[T]]
1566+
if typing.IsAssignable[typing.GetType[p], Property]
1567+
]
1568+
]
1569+
1570+
Everyone hated how this looked a lot.
1571+
15001572
.. _less_syntax:
15011573

15021574

@@ -1517,8 +1589,9 @@ Boolean operations would likewise become operators (``Not``, ``And``,
15171589
etc).
15181590

15191591
The advantage of this is that constructing a type annotation never
1520-
needs to do non-trivial computation, and thus we don't need
1521-
:ref:`runtime hooks <rt-support>` to support evaluating them.
1592+
needs to do non-trivial computation (assuming we also get rid of dot
1593+
notation), and thus we don't need :ref:`runtime hooks <rt-support>` to
1594+
support evaluating them.
15221595

15231596
It would also mean that it would be much easier to extract the raw
15241597
type annotation. (The lambda form would still be somewhat fiddly.
@@ -1535,35 +1608,6 @@ worse. Supporting filtering while mapping would make it even more bad
15351608

15361609
We can explore other options too if needed.
15371610

1538-
1539-
Don't use dot notation to access ``Member`` components
1540-
------------------------------------------------------
1541-
1542-
Earlier versions of this PEP draft omitted the ability to write
1543-
``m.name`` and similar on ``Member`` and ``Param`` components, and
1544-
instead relied on helper operators such as ``typing.GetName`` (that
1545-
could be implemented under the hood using ``typing.GetArg`` or
1546-
``typing.GetMemberType``).
1547-
1548-
The potential advantage here is reducing the number of new constructs
1549-
being added to the type language, and avoiding needing to either
1550-
introduce a new general mechanism for associated types or having a
1551-
special-case for ``Member``.
1552-
1553-
``PropsOnly`` (from :ref:`the query builder example <qb-impl>`) would
1554-
look like::
1555-
1556-
type PropsOnly[T] = typing.NewProtocol[
1557-
*[
1558-
typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1559-
for p in typing.Iter[typing.Attrs[T]]
1560-
if typing.IsAssignable[typing.GetType[p], Property]
1561-
]
1562-
]
1563-
1564-
Everyone hated how this looked a lot.
1565-
1566-
15671611
Perform type manipulations with normal Python functions
15681612
-------------------------------------------------------
15691613

@@ -1646,7 +1690,7 @@ arguments invariantly.
16461690
Acknowledgements
16471691
================
16481692

1649-
Jukka Lehtosalo
1693+
Jukka Lehtosalo, etc
16501694

16511695
[Thank anyone who has helped with the PEP.]
16521696

0 commit comments

Comments
 (0)