Generics alternative syntax

Hello all, Sorry to bother you again with the generic thing. Golang will soon release a new version with Generics. I like their way of doing it. Like Rust, they don’t bother with TypeVars. It makes you wonder why we are not doing something similar in Python. [T: Type] ——> T is invariant and Type is the bound. [T] is Ok. [T_co:: Type] ——> T is covariant and Type is the bound. [T_co::] is Ok. [T_contra::: Type] —-> T is contravariant and Type is the bound . [T_contra:::] is Ok. Or some other symbol next to : to distinguish the variance. [T: Type1 | Type2] —-> T is invariant and Type1 and Type2 are the constraints. Or anything else to communicate these are constraints (maybe enclose them in parentheses — tuple) Example: class Indexable[T_co::](Protocol[T_co]): def __getitem__(self, n: int) -> T_co: … @dataclass class AInt: seq: list[int] def __getitem__(self, n: int) -> Int: return self.seq[n] @dataclass class BStr: seq: list[str] def __getitem__(self, n: int) -> str: return self.seq[n] def get[T: int | str](n: int, object: Indexable[T]) -> T: return object[n] get(1, AInt([1, 2])) # ok get(1, BStr([“one”, “two”])) # ok Abdulla Sent from my iPhone

Abdulla Al Kathiri writes:
Typing syntax at present is required to be Python syntax. New typing syntax must be introduced to Python, so this is a huge hurdle and you probably won't get much sympathy here, because most of us have no idea what you're talking about, and the syntax looks awful if you don't know why you'd even want it. Even helpful discussion is going to be in short supply on this list (for example, I myself understand the definition of covariance when I read it, but I still have to look it up every time :-). Your audience is really in the Typing SIG. Happy hunting! Steve

I thought this is called python-ideas, meaning it’s ok to bring ideas even if they are ugly or stupid. No need to introduce it in Python if it’s too much but it might induce discussions. I am not per se convinced of the syntax I wrote. Any suggestion to make it better is more than welcomed, which I clearly stated in the post. I don’t know the covariance thing either but I put it there because TypeVar function has those keyword parameters. So if we are going to replace TypeVar, the alternative needs to have them as well. The reason this might be wanted is to reduce global variables for the TypeVars and make them contained/local to the class and function. When I encounter functions or classes with those TypeVars in people codes, I have to scroll up to see what they represent. Several languages are opting to make the generics bound to the function/class, so it’s not like what I am bringing here is completely insane. But I understand if the syntax is not suitable for python or if it will add unnecessary complexity to the developers and users for little gain. Abdulla

On Tue, 8 Feb 2022 at 18:59, Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
I thought this is called python-ideas, meaning it’s ok to bring ideas even if they are ugly or stupid. No need to introduce it in Python if it’s too much but it might induce discussions.
Yes, it's absolutely okay to bring ideas of all kinds; but every idea has consequences. What Stephen pointed out was a consequence that you perhaps hadn't been aware of, due to Python's type syntax being strictly the same as its expression syntax. There's no such thing as "impossible", but the more barriers there are for an idea, the more useful it has to be in order to be of interest. Introducing a new @ operator to allow you to create an EmailAddress class that uses it? Not interesting. Introducing a new @ operator to allow you to multiply numpy arrays in a different way? Significantly more interesting, even though the cost to the language is effectively the same. (And even then it took a long time to happen, because it broke the pattern that every operator is important to core data types.) What has to change for your idea to be able to happen?
class Indexable[T_co::](Protocol[T_co]): def __getitem__(self, n: int) -> T_co: …
This is completely new syntax: between the class name and what it subclasses, you have a subscript-like syntax. This is new semantics for subclassing, I think, unless you want Indexable to be a subclass of Protocol[T_co], but I'm not entirely sure how to read this. New semantics aren't as hard as new syntax, though.
def get[T: int | str](n: int, object: Indexable[T]) -> T: return object[n]
Perhaps unsurprisingly, the new syntax needs to work for functions as well as classes. So that's not really an additional cost, it's just the same cost seen in another place. What would be the semantics of this subscripting? What would be the meaning of, say, "def spam[42]():"? What if you leave the brackets empty? Cute syntax on its own doesn't make an idea; the cute syntax needs to be backed by a demonstration of how it's beneficial. ChrisA

Personally, I very much like this approach, although I'd definitely prefer <> brackets instead, like in other languages. We could possibly think of it as being syntactic sugar for the current TypeVar approach, and have it translated into TypeVars at runtime. However, if we'd define it that way, then these type variables would still end up in the global scope. We don't have scopes at the class/function definition level. That would mean that we can't reuse the same symbol for separate functions, unless the types are identical. Jonathan Le mar. 8 févr. 2022 à 09:15, Chris Angelico <rosuav@gmail.com> a écrit :

[T_co::] is not super class. The subclassing semantics remain the same. It’s subclassing Protocol[T_co]. But instead of defining T_co outside, we define it inside local to the class or function. Sometimes when constructing new objects, we use this same brackets to help the static tools to infer the type of the generic. Something like the following: T = TypeVar(“T", contravariant=True) @dataclass class A(Generic[T]): def method(self, x: T) -> None: print(f"type of x is {type(x)}") obj1 = A[int]() # equivalent to obj1: A[int] = A() obj2 = A[str]() # equivalent to obj2: A[str] = A() def str_only_func(x: str, obj: A[str]) -> None: obj.method(x) str_only_func(“hi", obj1) # Not Ok str_only_func(“hi", obj2) # Ok
"def spam[42]():" should throw a syntax error because 42 is not a valid variable name. You cannot assign to a literal. Remember between the brackets, we put the TypeVar, which by definition a type variable. Isn’t 42 = TypeVar(…) a syntax error? By extension, you can’t do ” = TypeVar(…)” without specifying the variable name, meaning empty brackets should throw an error as well. Abdulla

Abdulla Al Kathiri writes:
I thought this is called python-ideas,
It is, and if that's all you know, this is the *right* place to bring this kind of idea. I'm sorry, I should have been more clear about this.
meaning it’s ok to bring ideas even if they are ugly or stupid.
What I was trying to point out is that the Python typing experts are mostly not on this list. Most of us don't know that much about typing, and the folks who here are enthusiastic about it mostly seem (like you) to be informed by other communities' practice, not by Python's. Borrowing from other communities is a *good* thing for many reasons, but it makes it hard for me to give useful advice. It's my strong feeling that that's true of most of the folks here. On the other hand, you'll find (almost) all the qualified folks over in the Typing SIG, that's all I wanted to say.
The reason this might be wanted
If you want it and it works for other languages, that's reason enough to take a look at it, I don't need to know more than that. :-) I'm just not qualified beyond advising you that Typing SIG is a much better venue. Steve

*Your audience is really in the Typing SIG. Happy hunting!Steve* Until I read that line I thought I was reading Typing-SIG I was saying cool for once I found a discussion I completely follow, but dear me, it turned out to be -ideas. Btw every time I read discussions on Typing-SIG I'm like: sigh, what's happening. Else OP is a Typing-SIG regular. Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius

I really like the idea as a whole presented here. I have a few concerns here though: 1. The support for constraints overlaps with union which is undesireable, Jelle has previously suggested using TypeVar in (constraint1, constraint2, ...) which I think is much nicer. 2. The syntax for defining co/contravariance feels very clunky, have you seen https://github.com/python/typing/issues/813? I think this method of defining the variance is much neater.

Abdulla Al Kathiri writes:
Typing syntax at present is required to be Python syntax. New typing syntax must be introduced to Python, so this is a huge hurdle and you probably won't get much sympathy here, because most of us have no idea what you're talking about, and the syntax looks awful if you don't know why you'd even want it. Even helpful discussion is going to be in short supply on this list (for example, I myself understand the definition of covariance when I read it, but I still have to look it up every time :-). Your audience is really in the Typing SIG. Happy hunting! Steve

I thought this is called python-ideas, meaning it’s ok to bring ideas even if they are ugly or stupid. No need to introduce it in Python if it’s too much but it might induce discussions. I am not per se convinced of the syntax I wrote. Any suggestion to make it better is more than welcomed, which I clearly stated in the post. I don’t know the covariance thing either but I put it there because TypeVar function has those keyword parameters. So if we are going to replace TypeVar, the alternative needs to have them as well. The reason this might be wanted is to reduce global variables for the TypeVars and make them contained/local to the class and function. When I encounter functions or classes with those TypeVars in people codes, I have to scroll up to see what they represent. Several languages are opting to make the generics bound to the function/class, so it’s not like what I am bringing here is completely insane. But I understand if the syntax is not suitable for python or if it will add unnecessary complexity to the developers and users for little gain. Abdulla

On Tue, 8 Feb 2022 at 18:59, Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
I thought this is called python-ideas, meaning it’s ok to bring ideas even if they are ugly or stupid. No need to introduce it in Python if it’s too much but it might induce discussions.
Yes, it's absolutely okay to bring ideas of all kinds; but every idea has consequences. What Stephen pointed out was a consequence that you perhaps hadn't been aware of, due to Python's type syntax being strictly the same as its expression syntax. There's no such thing as "impossible", but the more barriers there are for an idea, the more useful it has to be in order to be of interest. Introducing a new @ operator to allow you to create an EmailAddress class that uses it? Not interesting. Introducing a new @ operator to allow you to multiply numpy arrays in a different way? Significantly more interesting, even though the cost to the language is effectively the same. (And even then it took a long time to happen, because it broke the pattern that every operator is important to core data types.) What has to change for your idea to be able to happen?
class Indexable[T_co::](Protocol[T_co]): def __getitem__(self, n: int) -> T_co: …
This is completely new syntax: between the class name and what it subclasses, you have a subscript-like syntax. This is new semantics for subclassing, I think, unless you want Indexable to be a subclass of Protocol[T_co], but I'm not entirely sure how to read this. New semantics aren't as hard as new syntax, though.
def get[T: int | str](n: int, object: Indexable[T]) -> T: return object[n]
Perhaps unsurprisingly, the new syntax needs to work for functions as well as classes. So that's not really an additional cost, it's just the same cost seen in another place. What would be the semantics of this subscripting? What would be the meaning of, say, "def spam[42]():"? What if you leave the brackets empty? Cute syntax on its own doesn't make an idea; the cute syntax needs to be backed by a demonstration of how it's beneficial. ChrisA

Personally, I very much like this approach, although I'd definitely prefer <> brackets instead, like in other languages. We could possibly think of it as being syntactic sugar for the current TypeVar approach, and have it translated into TypeVars at runtime. However, if we'd define it that way, then these type variables would still end up in the global scope. We don't have scopes at the class/function definition level. That would mean that we can't reuse the same symbol for separate functions, unless the types are identical. Jonathan Le mar. 8 févr. 2022 à 09:15, Chris Angelico <rosuav@gmail.com> a écrit :

[T_co::] is not super class. The subclassing semantics remain the same. It’s subclassing Protocol[T_co]. But instead of defining T_co outside, we define it inside local to the class or function. Sometimes when constructing new objects, we use this same brackets to help the static tools to infer the type of the generic. Something like the following: T = TypeVar(“T", contravariant=True) @dataclass class A(Generic[T]): def method(self, x: T) -> None: print(f"type of x is {type(x)}") obj1 = A[int]() # equivalent to obj1: A[int] = A() obj2 = A[str]() # equivalent to obj2: A[str] = A() def str_only_func(x: str, obj: A[str]) -> None: obj.method(x) str_only_func(“hi", obj1) # Not Ok str_only_func(“hi", obj2) # Ok
"def spam[42]():" should throw a syntax error because 42 is not a valid variable name. You cannot assign to a literal. Remember between the brackets, we put the TypeVar, which by definition a type variable. Isn’t 42 = TypeVar(…) a syntax error? By extension, you can’t do ” = TypeVar(…)” without specifying the variable name, meaning empty brackets should throw an error as well. Abdulla

Abdulla Al Kathiri writes:
I thought this is called python-ideas,
It is, and if that's all you know, this is the *right* place to bring this kind of idea. I'm sorry, I should have been more clear about this.
meaning it’s ok to bring ideas even if they are ugly or stupid.
What I was trying to point out is that the Python typing experts are mostly not on this list. Most of us don't know that much about typing, and the folks who here are enthusiastic about it mostly seem (like you) to be informed by other communities' practice, not by Python's. Borrowing from other communities is a *good* thing for many reasons, but it makes it hard for me to give useful advice. It's my strong feeling that that's true of most of the folks here. On the other hand, you'll find (almost) all the qualified folks over in the Typing SIG, that's all I wanted to say.
The reason this might be wanted
If you want it and it works for other languages, that's reason enough to take a look at it, I don't need to know more than that. :-) I'm just not qualified beyond advising you that Typing SIG is a much better venue. Steve

*Your audience is really in the Typing SIG. Happy hunting!Steve* Until I read that line I thought I was reading Typing-SIG I was saying cool for once I found a discussion I completely follow, but dear me, it turned out to be -ideas. Btw every time I read discussions on Typing-SIG I'm like: sigh, what's happening. Else OP is a Typing-SIG regular. Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius

I really like the idea as a whole presented here. I have a few concerns here though: 1. The support for constraints overlaps with union which is undesireable, Jelle has previously suggested using TypeVar in (constraint1, constraint2, ...) which I think is much nicer. 2. The syntax for defining co/contravariance feels very clunky, have you seen https://github.com/python/typing/issues/813? I think this method of defining the variance is much neater.
participants (6)
-
Abdulla Al Kathiri
-
Abdur-Rahmaan Janhangeer
-
Chris Angelico
-
James H-B
-
Jonathan Slenders
-
Stephen J. Turnbull