Skip to content
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

Make Mapping covariant in keys (too). #273

Closed
wants to merge 1 commit into from

Conversation

gvanrossum
Copy link
Member

@ilevkivskyi
Copy link
Member

@gvanrossum
I am not sure that I am comfortable with this. For example consider this code:

def fun(m: Mapping[float, Any]):
    return m[0.1]

class StrangeMap(Mapping[int, Any]):
    def __getitem__(self, item):
        return item << 5

fun(StrangeMap())

If Mapping would be covariant this will typecheck, since int is a subtype of float, therefore StrangeMap is a subtype of Mapping[float, Any]. But this code will raise TypeError at runtume.

Of course this example is a bit artificial, but I think there are realistic examples just going the same way.

@gvanrossum
Copy link
Member Author

@JukkaL What do you think?

On Wed, Aug 31, 2016 at 11:56 AM, Ivan Levkivskyi notifications@github.com
wrote:

@gvanrossum https://github.com/gvanrossum
I am not sure that I am comfortable with this. For example consider this
code:

def fun(m: Mapping[float, Any]):
return m[0.1]
class StrangeMap(Mapping[int, Any]):
def getitem(self, item):
return item << 5

fun(StrangeMap())

If Mapping would be covariant this will typecheck, since int is a subtype
of float, therefore StrangeMap is a subtype of Mapping[float, Any]. But
this code will raise TypeError at runtume.

Of course this example is a bit artificial, but I think there are
realistic examples just going the same way.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#273 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ACwrMurtnwGQvnWtuWVYkyQccwQhQOq8ks5qlc5EgaJpZM4JxET9
.

--Guido van Rossum (python.org/~guido)

@JukkaL
Copy link
Contributor

JukkaL commented Aug 31, 2016

As discussed in python/typeshed#510, this is clearly unsafe, but maybe the cases where the unsafety might manifest itself are more common that I originally thought :-(

Making it covariant in the value type should be less of a problem. We could use a separate type variable _T for the second argument of get and return Union[_T, _VT], but mypy might not be happy about this definition yet.

@gvanrossum
Copy link
Member Author

OK, I'll close this for now, leaving Mapping covariant in the value but not in the type. (Alas, mypy doesn't understand that union yet, so get() still requires # type: ignore.)

@Prometheus3375
Copy link

If Mapping would be covariant this will typecheck, since int is a subtype of float

int is not a subtype of float at least in Python 3.9.1.

>>> issubclass(int, float)
False
>>> isinstance(1, float)
False

@gvanrossum
Copy link
Member Author

int is not a subtype of float at least in Python 3.9.1.

But it is in for static type checkers, and this is specified by PEP 484.

@rhshadrach
Copy link

Is the only issue making Mapping covariant in the key with int/float? For example, I expect Mapping[A, C] to be compatible with Mapping[Union[A, B], C]. Looking at other issues, one other reason I've seen for not making Mapping covariant in the key is that a subclass can be mutable when the superclass is not. This seems to me to fall under "We're all adults here".

Tangential question - is the covariance/contravariance specified by a PEP? I couldn't find anything other than PEP 484 which says:

The read-only collection classes in typing are all declared covariant in their type variable (e.g. Mapping and Sequence). The mutable collection classes (e.g. MutableMapping and MutableSequence) are declared invariant. The one example of a contravariant type is the Generator type, which is contravariant in the send() argument type (see below).

which seems to indicate Mapping should be covariant in the key (but is slightly ambiguous, I suppose).

If I've captured the objections to making Mapping covariant in the key, then my take is that not making Mapping covariant in the key is foregoing a generally useful feature in good code for oddities some might experience in not-so-good code. But I suspect I may be missing some cases.

@ktbarrett
Copy link

ktbarrett commented Dec 6, 2021

Whole-type variance is not fine-grained enough to describe the relation here. Indexing is actually contravariant, setting is covariant as are other operations. The variance of the type, as it appears on a function, depends entirely on how the object is used. This is also related to python/typeshed#7121 and python/typeshed#7015 where methods are contravariant, but the type is covariant.

There is no way to describe this with Python's type system, as it stands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants