After the discussion on type var defaults, Guido and I had a short
discussion about a potential inline-definition syntax for type vars.
Such a syntax has a few advantages over the definition using TypeVar:
* The type vars would be defined close to where they are actually used.
* Type vars would not leak their name into the module-global scope.
* Removes the duplication from TypeVar: _T = TypeVar("_T")
* This is in line with what other languages are doing and so more
familiar for developers coming from other languages.
Even if we implement an inline syntax, TypeVar should not be deprecated
as it's still quite useful for defining re-usable type vars like
typing.AnyStr.
Here are a few proposals we came up with.
Inline syntax using square brackets
=====================
class C[T, S]:
def foo[R=int](self) -> R: pass
Some ideas how we could express upper bounds, constraints, and variance:
* Upper bounds: [R(Base1 | Base2)] or [R from Base1 | Base2].
* Constraints: [R as Base1 | Base2]
* Variance: [R(covariant=True)]
Advantages:
* Similar syntax to existing generics syntax.
* Used by languages such as Go and Scala.
Disadvantages:
* Square brackets are already used for indexes/slices and generics.
* We have a bit of a "bracket hell", which does not help readability:
def foo[F=dict[str, Callable[[int], object]]](): pass
Inline syntax using angle brackets
=====================
class C<T, S>:
def foo<R>(self) -> R: pass
Advantages:
* Angle brackets are currently unused in Python and lower than/greater
than is not used within type context.
* Using angle brackets could reduce "bracket hell".
* Used by languages like C++, Java, C#, TypeScript, Delphi, so should be
fairly similar to developers coming from popular languages.
Disadvantages:
* Slightly inconsistent with existing generic syntax.
Decorators
=======
@typevar("T")
@typevar("S")
class C:
@typevar("R", default=int)
def foo(self) -> R: pass
Advantages:
* Doesn't require syntax changes to Python.
* Very readable (in my opinion), because it has one type var per line,
especially with more complex type vars.
* Is defined exactly like a TypeVar(), but without assigning it to a name.
* Flexible and extensible as it's using regular decorator syntax.
* Backwards compatible as it could be added to typing_extensions.
Disadvantages:
* More verbose than the other suggestions, especially when using simple
type vars.
* Non-standard syntax, compared to other languages.
* Currently requires either quoted types or "from __future__ import
annotations".
-------------------------
My personal favorite is the decorator syntax, for all the advantages
listed. The disadvantages seem minor to me or temporary. Being more
verbose makes is actually more readable. When writing TypeScript, I
often have monsters like this:
function createReactStore<
S,
A extends Action = AnyAction,
E = undefined,
>(
someArguments: ...
) {
// ...
}
I believe that the Python equivalent using decorator would be more readable:
@typevar("S")
@typevar("A", bound=Action, default=AnyAction)
@typevar("E", default=object)
def createReactStore(
someArguments: ...
):
...
- Sebastian
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: sergei.a.lebedev@gmail.com