New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce an Intersection #213
Comments
|
Mypy complains about your code because I'm worried that intersection types would be tricky to implement in mypy, though conceptually it should be feasible. I'd prefer supporting structural subtyping / protocols -- they would support your use case, as |
|
It would be really cool to implement protocols. Still, in this case intersection could be added as a "syntactic sugar", since there would be a certain asymmetry: Assume you want a type alias for something that implements either protocol, then you write: class _Intersection:
def __getitem__(self, bases):
full_bases = bases+(Protocol,)
class Inter(*full_bases): ...
return Inter
Intersection = _Intersection()then one could write: |
|
|
|
I understand what you mean. That could be indeed tricky in general case. Concerning protocols, I think structural subtyping would be quite natural for Python users, but only practice could show whether it will be useful. I think it will be useful. |
|
This keeps coming up, in particular when people have code that they want to support both sets and sequences -- there is no good common type, and many people believe Iterable is the solution, but it isn't (it doesn't support |
|
I think I don't think that one needs to choose between protocols and It is easy to add |
|
For cross-reference from #2702, this would be useful for type variables, e.g. |
|
|
|
If we had an intersection class in
|
|
As a data point, I first looked for |
|
Just as a random idea I was thinking about T = TypeVar('T')
CBack = Optional[Callable[[T], None]]
def process(data: bytes, on_error: CBack[bytes]) -> None:
... |
|
I just opened #483 hoping for exactly the same thing. I literally named it the same. I would be all for |
|
Requests for |
|
I think we should note the use cases but not act on it immediately -- there
are other tasks that IMO are more important.
|
OK, I will focus now on PEP 560 plus related changes to Btw, looking at the milestone "PEP 484 finalization", there are two important issues that need to be fixed soon: #208 ( |
|
I agree that now's not the right time to add intersection types, but it may make sense later. |
|
(been redirected here from the mailing list) I think the Intersection[Any, Not[None]]Would mean anything but |
|
How about the expression example:
|
|
@rnarkk these have already been proposed many times, but have not been accepted. |
|
The What's the use case for that? It's not something I've ever missed in a medium-sized typed codebase at work and in lots of work on typeshed. |
No. I accept that there could be infinitely many types that satisfy Any, but that's different from saying that those infinitely many types have a known interface known to contain f The definition of an intersection here is that things that satisfy the intersection are the things that satisfy the types of all of the member types or protocols of the intersection. Where each portion comes from is irrelevant to this. T & Any isn't satisfied by an unrelated Q, because Q doesn't satisfy T. The requirement of satisfying T is why it doesn't reduce to Any, the fiction that Any could potentially contain every interface which effectively disables type checking with some settings is irrelevant to satisfying the requirement of T |
I don't understand. If def function(x: Any):
x.f()No type checker checks (or is supposed to check) whether "those infinitely many types have a known interface known to contain f". I think this is where the misunderstanding is happening.
Please let's stick to the examples. We obviously disagree on intuitions. |
|
Okay, let me put this in very simple terms Any is always satisfied by an object The requirement to satisfy the requirements of Any can be removed as trivial, as they are always satisfied, but an item being checked against an intersection has to be a member of each component of the intersection (this is the entire rationale for an intersection, and the definition matches with the proposal above) In set theory terms, Any is the set of all types Given a type specification T, and instance of an object P, P is a member of the intersection of T and Any if and only if P is a member of T |
The problem is that you are confusing the universal base class It is true that every type is compatible with both The best way to analyze these problems is to focus on examples. |
I quite literally didn't ignore this, I just said it's irrelevant to the other operand An Intersection
I gave clear examples of this that don't use Any to demonstrate the motivations of an intersection as given in the post. If you go to the very beginning, the motivating example is two abstract base classes that both need implementing. Clearly both must be satisfied. Any being always satisfied doesn't say anything about other non-any requirements. |
|
We're going to go around in circles if you keep repeating your intuitions. Please let's focus on the actual examples which we left off in this comment. |
Fine, I'll bite. The example you linked doesn't contain an intersection at all and isn't a good representative of the issue I described. I'm not speaking about just "intuitions" but the literal definition in the thread of an intersection and the motivations of them. your example that you claim has no errors fails when using settings in mypy that exist specifically to detect this kind of problem as well as at runtime. But It's irrelevant. There's no intersection here. def function(x: Any):
x.f()If I instead modify it to def function(x: Any & T):
...The requirements of x are now "Is all of the types: Any, T" (see original post, current draft, etc) While Any can be anything, there are things that are not T The original motivating bit on this at the very beginning of this, years ago is "how do I specify I require that people have implemented both of these abstract base classes" (paraphrased) not one or the other (this would just be "just use Union") but both |
I know that there's no intersection. But you said:
How can that code pass type checking if |
|
@NeilGirdhar, it seems you may have misinterpreted @kmillikin's comment to be more general than it actually was. You appear to have conflated the definitions of Unions and Intersections, which is why @mikeshardmind is helping clarify the definitions for you - this is also why "sticking to examples" won't help. |
|
I think this discussion is rapidly becoming unproductive and you should both take a break here. I'll just say that as far as I can tell @mikeshardmind is right about how intersections should operate. An object is compatible with |
Doesn't that mean that Any & T is always Any? In certain language if(A && B) satify A, B won't even be checked, so in Any & T, why would we need to satisfy anything after Any? From what I remember one of the aspect of Python is assuming that we are grown up. And if someone want to add an Any somewhere and it fuck the intersection, let it be (or ask mypy to check for no Any in the code). Implementation wise maybe there is a specific condition that make it easier to discard Any in type intersection but it's counter intuitive. |
I think you may have confused This issue is about adding an intersection type and the whole point of it is for the variable to have to satisfy all of the specified types at the same time as opposed to the already existing union type. Within the definition provided by Python's type theory (see PEP 483) as well as the PEP draft, the intersection is defined as "a type that is a subtype of all of its arguments" which is consistent with union's definition of being "a type that is a subtype of at least one of its arguments". |
|
Hello everybody, I agree with @JelleZijlstra that discussion became unproductive, also simply to do the fact github issues become become very unreadable with a large amount of comments. Nobody can follow the discussion here as not even all comments are loaded by default and they are undirected because there are no threads. I will try to deduct a logical chain of expressions about each discussed topic here and create an Issues for each in my example repo. For the following topics, you can discuss in the related issues, please note the instructions in each issue on how to contribute: |
|
I just want to point out that, practically speaking, there are scenarios where it would be very frustrating if having Suppose I have: def foo(x: A & B): ...I want to make sure that whatever is passed in has both A and B's interface. That's the entire point of using the intersection there, To control what can be passed in without tripping the type checker. Now suppose B is imported from some 3rd party library, and unbeknownst to me is actually a type alias to Now my type annotation has been completely obliterated. Even if B could be anything I still want the invariant that whatever goes in there conforms to A. If an intersection with Am I expecting the wrong thing here? I absolutely do not want |
Current State of Intersections in PythonThere are a bunch of people working on a PEP and a the specification for Intersection now. This issues here is very old and very long and handles a lot of different things that have already been moved to different issues i.e. Introduce a We keep the current WIP specification in an extra repo. Could we maybe lock the discussion on this issue? Further discussion should go into the correct sub issues related to the specification. |
This question has already been discussed in #18 long time ago, but now I stumble on this in a practical question: How to annotate something that subclasses two ABC's. Currently, a workaround is to introduce a "mix" class:
but mypy complains about this
But then I have found this code snippet in #18
Which is exactly what I want, and it is also much cleaner than introducing an auxiliary "mix" class. Maybe then introducing
Intersectionis a good idea, @JukkaL is it easy to implement it in mypy?The text was updated successfully, but these errors were encountered: