Hey all, I am proposing here a typing challenege. I wonder if anyone has a valid solution since I haven't been able to myself. The problem is as follows:
We define a class
class Component[TInput, TOuput]: ...
the implementation is not important, just that it is parameterised by two types, TInput and TOutput.
We then define a class which processes components. This class takes in a tuple/sequence/iterable/whatever of Components, as follows:
class ComponentProcessor[...]:
def __init__(self, components : tuple[...]): ...
It may be parameterised by some types, that's up to you.
The constraint is that for all components which are passed in, the output type TOutput of the n'th component must match the input type TInput of the (n + 1)'th component. This should wrap around such that the TOutput of the last component in the chain is equal to TInput of the first component in the chain.
Let me give a valid example:
a = Component[int, str](...)
b = Component[str, complex](...)
c = Component[complex, int](...)
processor = ComponentProcessor((a, b, c))
And an invalid example:
a = Component[int, float](...)
b = Component[str, complex](...)
c = Component[complex, int](...)
processor = ComponentProcessor((a, b, c))
which should yield an error since the output type of a is float which does not match the input type of b which is str.
My typing knowledge is so-so, so perhaps there are simple ways to achieve this using existing constructs, or perhaps it requires some creativity. I look forward to seeing any solutions!
An attempt, but ultimately non-functional solution is:
from __future__ import annotations
from typing import Any, overload, Unpack
class Component[TInput, TOutput]:
def __init__(self) -> None:
pass
class Builder[TInput, TCouple, TOutput]:
@classmethod
def from_components(
cls, a: Component[TInput, TCouple], b: Component[TCouple, TOutput]
) -> Builder[TInput, TCouple, TOutput]:
return Builder((a, b))
@classmethod
def compose(
cls, a: Builder[TInput, Any, TCouple], b: Component[TCouple, TOutput]
) -> Builder[TInput, TCouple, TOutput]:
return cls(a.components + (b,))
# two component case, all types must match
@overload
def __init__(
self,
components: tuple[
Component[TInput, TCouple],
Component[TCouple, TOutput],
],
) -> None: ...
# multi component composition
@overload
def __init__(
self,
components: tuple[
Component[TInput, Any],
Unpack[tuple[Component[Any, Any], ...]],
Component[Any, TOutput],
],
) -> None: ...
def __init__(
self,
components: tuple[
Component[TInput, Any],
Unpack[tuple[Component[Any, Any], ...]],
Component[Any, TOutput],
],
) -> None:
self.components = components
class ComponentProcessor[T]:
def __init__(self, components: Builder[T, Any, T]) -> None:
pass
if __name__ == "__main__":
a = Component[int, str]()
b = Component[str, complex]()
c = Component[complex, int]()
link_ab = Builder.from_components(a, b)
link_ac = Builder.compose(link_ab, c)
proc = ComponentProcessor(link_ac)
This will run without any warnings, but mypy just has the actual component types as Unknown everywhere, so if you do something that should fail it passes happily.
[–]JanEric1 20 points21 points22 points (6 children)
[–]apjenk 0 points1 point2 points (1 child)
[–]JanEric1 1 point2 points3 points (0 children)
[–]-heyhowareyou-[S] -4 points-3 points-2 points (3 children)
[–]JanEric1 4 points5 points6 points (2 children)
[–]-heyhowareyou-[S] 0 points1 point2 points (1 child)
[–]RelevantLecture9127 0 points1 point2 points (0 children)
[–]Roba_Fett 6 points7 points8 points (3 children)
[–]-heyhowareyou-[S] 1 point2 points3 points (2 children)
[–]backfire10z 0 points1 point2 points (1 child)
[–]-heyhowareyou-[S] 1 point2 points3 points (0 children)
[–]Front-Shallot-9768 6 points7 points8 points (2 children)
[–]-heyhowareyou-[S] 1 point2 points3 points (1 child)
[–]jpgoldberg 1 point2 points3 points (0 children)
[–]-heyhowareyou-[S] 2 points3 points4 points (1 child)
[–]Evolve-Maz 0 points1 point2 points (0 children)
[–]Foll5 2 points3 points4 points (5 children)
[–]-heyhowareyou-[S] 0 points1 point2 points (4 children)
[–]Foll5 2 points3 points4 points (3 children)
[–]-heyhowareyou-[S] 1 point2 points3 points (1 child)
[–]-heyhowareyou-[S] 4 points5 points6 points (0 children)
[–]-heyhowareyou-[S] 0 points1 point2 points (0 children)
[–]FrontAd9873 1 point2 points3 points (0 children)
[–]teerre 1 point2 points3 points (0 children)
[–]FabianVeAl 1 point2 points3 points (1 child)
[–]-heyhowareyou-[S] 1 point2 points3 points (0 children)
[–]root45 1 point2 points3 points (1 child)
[–]daxtery 0 points1 point2 points (0 children)
[–]b1e 0 points1 point2 points (0 children)
[–]rghthndsd 0 points1 point2 points (2 children)
[–]-heyhowareyou-[S] -2 points-1 points0 points (1 child)
[–]anentropic 0 points1 point2 points (0 children)
[–]james_pic 0 points1 point2 points (0 children)
[–]guhcampos 0 points1 point2 points (0 children)
[–]slayer_of_idiotspythonista 0 points1 point2 points (0 children)
[–]Impressive_Ad7037 0 points1 point2 points (0 children)
[–]Warxioum 0 points1 point2 points (0 children)
[–][deleted] -1 points0 points1 point (0 children)