No project description provided
Project description
type-intersections
A naive prototype of type intersections implementation.
Installation
pip install type-intersections
Intro
Type intersections are a way for a programmer to say that some variable is of type A
and type B
at the same time which is only possible if it inherits from A
and B
or if A
and B
are both structural types with overloaded subclass checks such as Sized
or Hashable
.
Current issues
Ordering checks
The current implementation assumes that intersections are unordered which makes some sense for two reasons:
- I have not been able to come up with an algorithm that can intuitively do structural and ordered intersection issubclass checks at the same time (see the XFAIL testcase). Not sure if that is even possible but I haven't tried for long.
- TypeScript doesn't even try to handle order in intersections
The issue arises when we try to decide what to use in issubclass checks: the class itself or its mro()
.
Let's say that a class C
inherits method foo()
that is defined in both its parents: A
and B
. Let's also say that C
defines the method __len__
that its parents do not have. If we have an intersection A & B & Sized
, what algorithm would we use to check that C
uses the correct foo()
and that it is Sized?
class A:
def foo(self, a):
print(a)
class B:
def foo(self, a, b):
print(a, b)
class C(A, B):
# Makes C Sized
def __len__(self):
return 83
-
If we try to just check
issubclass(C, A) and issubclass(C, B) and issubclass(C, Sized)
, then we are omitting order information and cannot definitively tell thatC
will inheritfoo()
fromA
. -
An alternative would be to go through
C
's MRO in order: checking that each next item in the MRO is a subclass of each argument in the intersection like so:
def __subclasscheck__(self, cls):
args = self.__args__
for type_ in cls.mro():
while args:
if issubclass(type_, args[0]):
args.pop(0)
else:
break
return not args
But we quickly realize that the first item in MRO is the class itself which is definitely an instance of A
, B
, and Sized
, thus keeping our check unordered. The naive way to resolve this would be to remove the class itself from its MRO before we start checking:
def __subclasscheck__(self, cls):
args = self.__args__[1:]
for type_ in cls.mro():
while args:
if issubclass(type_, args[0]):
args.pop(0)
else:
break
return not args
But now issubclass(C, Intersection[A, B, Sized])
is False
because __len__
is defined on C
which we skip in the MRO checks.
Another option would be to use both algorithms: unordered algorithm for structural checks and ordered algorithm for inheritance-based checks. But we cannot distinguish between the checks because classes can have their __subclasscheck__
redefined in all sorts of complex ways and because even some structural checks care about order: such as in the example I gave above from TypeScript.
In other words, we can't check that some class C
inherits from classes A
and B
in this specific order (i.e. not from B
and A
) and check that it implements Sized
at the same time. The only solution I see is to abandon either structural checks or order in inheritance checks. Order seems a lot less necessary and fundamental so I decided to abandon it for now for my naive implementation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for type_intersections-0.1.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d4126b782c0fd543041c0d3e50aa31101ccf6df7d1939cb2573ccda5793db213 |
|
MD5 | 47163a0ba73cadc2eba95fb55da7c3d9 |
|
BLAKE2b-256 | aae27572032fa4c0809d158a57d0a0998a4ca5cf662e5aeb3c1e5d15055995ec |