Simple typed mux#262
Conversation
clint-lawrence
left a comment
There was a problem hiding this comment.
This looks pretty good. The implementation is so much simpler, not having to rely on the run time type inspection to build the mux.
If it read it right, when there is a mismatch between the signals in the type and the signals used for the map_list, you would get a type error
type Signals = Literal["signal_1", "signal_2"]
class MyTypedMux(VirtualMux[Signals]):
pin_list = ("x0", "x1")
map_list = (
("signal_1", "x0"),
("bad_signal", "x1"), # <- this would be a type error
)Have I understood that correctly?
| # the type keyword can be used to create reusable definitions | ||
| # otherwise Literal can be used directly | ||
| type Signals = Literal["signal_1", "signal_2"] | ||
|
|
||
| # note: the type keyword can't be used inside functions! | ||
| # generally we want to use type to avoid confusion around the type system | ||
| # this makes it clear we are creating something for typehinting | ||
| # e.g type MyInt = int - won't work in functions | ||
| # variable = int - is not obvious what the intent is and can behave differently depending on its scope | ||
|
|
||
|
|
||
| def do_some_stuff(): | ||
| # otherwise the mux is created as normal | ||
| class MyTypedMux(VirtualMux[Signals]): | ||
| pin_list = ("x0", "x1") | ||
| map_list = ( | ||
| ("signal_1", "x0"), | ||
| ("signal_2", "x1"), | ||
| ) | ||
|
|
||
| mymux = MyTypedMux() |
There was a problem hiding this comment.
| # the type keyword can be used to create reusable definitions | |
| # otherwise Literal can be used directly | |
| type Signals = Literal["signal_1", "signal_2"] | |
| # note: the type keyword can't be used inside functions! | |
| # generally we want to use type to avoid confusion around the type system | |
| # this makes it clear we are creating something for typehinting | |
| # e.g type MyInt = int - won't work in functions | |
| # variable = int - is not obvious what the intent is and can behave differently depending on its scope | |
| def do_some_stuff(): | |
| # otherwise the mux is created as normal | |
| class MyTypedMux(VirtualMux[Signals]): | |
| pin_list = ("x0", "x1") | |
| map_list = ( | |
| ("signal_1", "x0"), | |
| ("signal_2", "x1"), | |
| ) | |
| mymux = MyTypedMux() | |
| # note: the type keyword can't be used inside functions! | |
| # generally we want to use type to avoid confusion around the type system | |
| # this makes it clear we are creating something for typehinting | |
| # e.g type MyInt = int - won't work in functions | |
| # variable = int - is not obvious what the intent is and can behave differently depending on its scope | |
| # the type keyword can be used to create reusable definitions | |
| # otherwise Literal can be used directly | |
| type MyTypedMuxSignals = Literal["signal_1", "signal_2"] | |
| def do_some_stuff(): | |
| # otherwise the mux is created as normal | |
| class MyTypedMux(VirtualMux[MyTypedMuxSignals]): | |
| pin_list = ("x0", "x1") | |
| map_list = ( | |
| ("signal_1", "x0"), | |
| ("signal_2", "x1"), | |
| ) | |
| mymux = MyTypedMux() |
If you move the type definition closer and give it a less general name, I think that would help people see the connection between the two.
| myrelay.multiplex("") | ||
| myrelay.multiplex("signal_1") | ||
| myrelay.multiplex("signal_2") | ||
|
|
There was a problem hiding this comment.
Could also show a concrete example of the alternate you hint at in the comment above
class MyTypedMux(VirtualMux[Literal["signal_1", "signal_2"]]):
pin_list = ("x0", "x1")
map_list = (
("signal_1", "x0"),
("signal_2", "x1"),
)
frozendict in python 3.15 looks promising, having to remind the typechecker that my dict of string literals is not just an arbitrary collection of strings feels clunky. TypeForm might also help with what I'm currently getting stuck on with wanting to do TypeGuards on generic types (such as a user supplied signal definition instead of the base string). Currently the way I'm trying to get around this is by putting it inside the VirtualMux class. For now things are in a slightly awkward spot where I want to use the type system, but that means I can't use isinstance for any logical type checks. |

I like this approach more than #188
Annotated never felt quite right. I'm going to go ahead with the approach of making the user still have to define the mapping between signals and pins themselves. Having to chain Union and Literal together also was clunky as Literal is already kind of like a union of options.
In the future this will be easier to add features to, e.g pin typing, and hinting for constructing the map or tree.
The type hints can also eventually be digested to not have to repeat the pin list.