1

I'd like to define a generic type. Something like:

from typing import TypeVar, Sequence, Union, Generic

T = TypeVar('T')
RecurSeqOf = Sequence[Union[Generic[T], Sequence[T]]]
# mypy error: Variable "typing.Generic" is not valid as a type

Is there a way to do it?

The whole background

Actually, I need a recursive generic type like

T = TypeVar('T')
RecurSeqOf = Sequence[Union[T, 'RecurSeqOf']]]

But the definition of recursive types is not yet supported by mypy.

That's why I work around this issue by making nested type definitions up to a limited depth (say, usually 5-6 levels, but in the example below two levels only for the sake of conciseness). Hence, all the more the need to shorten the pattern because I need to use it for different parameter types:

from typing import Sequence, Union, TypeVar, Generic


class A:
    pass


class B:
    pass


# RecurSeqOfA = Sequence[Union[A, 'RecurSeqOfA']]  # mypy error: Cannot resolve name "RecurSeqOfA" (possible cyclic definition)
RecurSeqOfA = Sequence[Union[A, Sequence[Union[A, Sequence[A]]]]]

# RecurSeqOfA = Sequence[Union[A, 'RecurSeqOfA']]  # mypy error: Cannot resolve name "RecurSeqOfA" (possible cyclic definition)
RecurSeqOfB = Sequence[Union[B, Sequence[Union[B, Sequence[B]]]]]

T = TypeVar('T')
# RecurSeqOf = Sequence[Union[Generic[T], 'RecurSeqOf']]  # error: Cannot resolve name "RecurSeqOf" (possible cyclic definition)
# additionally: error: Variable "typing.Generic" is not valid as a type
RecurSeqOf = Sequence[Union[Generic[T], Sequence[Generic[T]]]]  # error: Variable "typing.Generic" is not valid as a type

As suggested by the comment of MisterMiyagi:

from typing import TypeVar, MutableSequence

T = TypeVar('T', bound='RecurSeqOf')
RecurSeqOf = MutableSequence[T]

a: RecurSeqOf[str] = []
a.append("abc")
a.append([])  # mypy error: error: Argument 1 to "append" of "MutableSequence" has incompatible type "List[<nothing>]"; expected "str"
b: RecurSeqOf[str] = []
a.append(b)  # mypy error: Argument 1 to "append" of "MutableSequence" has incompatible type "MutableSequence[str]"; expected "str"
a.append(["cde"])  # mypy error: Argument 1 to "append" of "MutableSequence" has incompatible type "List[str]"; expected "str"

The definition itself is accepted by mypy. But it does not have the desired effect.

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Min-Soo Pipefeet
  • 2,208
  • 4
  • 12
  • 31
  • Can you clarify what you think a ``Sequence[Generic[T]]`` is? ``Generic[T]`` does not denote any sensible type, just like a bare ``NewType`` or empty ``Union``. Do you mean a "``Sequence[T]``" or a "``Sequence[X[T]]`` for some/any ``X``"? Have you considered to use a "``RecurSeqOf = Sequence[T]`` where ``T = TypeVar("T", RecurSeqOf)``"? – MisterMiyagi Feb 25 '21 at 08:55
  • @MisterMiyagi Thx for the suggestion. I tried out and included into the question. By `Sequence[Generic[T]]` I simply mean the declaration of the generic type `Sequence` that can be used as `Sequence[int]` or `Sequence[str]` and so on. `Sequence[Generic[T]]` is not needed, as it is the same as the existing `typing.Sequence`. The need arises first if the sequenced type needs to be recursive. So, your suggestion hits the problem precisely. – Min-Soo Pipefeet Feb 25 '21 at 09:25
  • @MisterMiyagi Oh, now I see why you asked what I think what `Sequence[Generic[T]]` is! Yes, it is indeed a syntactic error of mine. I started to remove `Generic` and found that the problem asked disappears with it. So, I let my question not completely corrected, so the commented errors and your answer are still comprehensible and fit together. Thx. – Min-Soo Pipefeet Feb 25 '21 at 09:44

1 Answers1

1

Since Sequence is already generic, one can directly use a type variable:

from typing import TypeVar, Sequence, Union

T = TypeVar('T')
# [T, ...] | [[T, ...], ...]
RecurSeqOf = Sequence[Union[T, Sequence[T]]]
# T | [T, ...] | [[T, ...], ...]
RecurSeqOfUnion = Union[RecurSeqOf[T], T]

This is what the documentation calls a "User defined generic type alias". RecurSeqOf = ... defines an alias, and Sequence[Union[T, Sequence[T]]] is generic.


This allows to define recursive types of fixed but arbitrary depth:

a0: RecurSeqOf[int]
a1: RecurSeqOf[RecurSeqOfUnion[int]]
a2: RecurSeqOf[RecurSeqOfUnion[RecurSeqOfUnion[int]]]
reveal_type(a0)  # typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]]
reveal_type(a1)  # typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]]
reveal_type(a2)  # typing.Sequence[Union[typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]], typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]], typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]]
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119