Coding 2023-04-14
Okay, let's take a little time to consider how these decorators are supposed to work. Ignoring MappedParametric for now, because that's a whole thing, I've got two basic options for external interfaces to use:
- @_parametric.parametric and @_parametric.adaptive
- @_parametric.Parametric.make and @_parametric.AdaptiveParametric.make
Sadly, I do kind of need to consider the lengths involved, since there are valid reasons to use these inline as well as in decorator form.
MappedParametric is kind of an opposite situation, where I'm not quite sure what I want out of the decorator, but there's no reason to care how the decorator works inline when I can just use the default attrs constructor.
I think the first question comes down to how committed I am to having all normal modules import modules instead of importing non-module items from modules. I don't remember the rationale for that, so it's time to try to go back to first principles, apparently. Now, where did I get that idea...
Let's see, my source was certainly the Google Python Style Guide, which, granted, I don't follow perfectly even in this context... Between that, and import this, and a desire to not given in completely to Pokémon-speak, here is my general idea:
- Decorate to produce a Parametric with @_parametric.make, as well as a few other module-level functions that can be used as a decorator.
- However, also consider adding fluent methods that forward to these functions when applicable. For example, I may want a .flattened method or property corresponding to the flatten function.
- Use adaptive for AdaptiveParametric.
- Use either one or two decorators for MappedParametric. This is the remaining effort I need to put into thinking about this.
Perhaps @_parametric.map_over(func)... No. Here's a slightly wonky idea...
@overload
def map_over(
func: Callable[[T], PathStr], /
) -> Callable[[Parametric[T]], MappedParametric[T]]:
...
@overload
def map_over(
parametric: Parametric[T], /
) -> Callable[[Callable[[T], PathStr]], MappedParametric[T]]:
...
def map_over(
func_or_parametric: Callable[[T], PathStr] | Parametric[T], /
) -> (
Callable[[Parametric[T]], MappedParametric[T]]
| Callable[[Callable[[T], PathStr]], MappedParametric[T]]
):
if isinstance(func_or_parametric, Parametric):
def needs_func(func: Callable[[T], PathStr]) -> MappedParametric[T]:
return MappedParametric(func_or_parametric, func)
return needs_func
def needs_parametric(parametric: Parametric[T]) -> MappedParametric[T]:
return MappedParametric(parametric, func_or_parametric)
return needs_parametric
Can't really use it inline, but as I said, you don't need to, and it types cleanly enough, and saves you the bother of having to remember which way around things go by having them both be right.
In fact, I'll try to get it to work now.
...
Hm, it didn't quite work, but it was close. Just needed to make some minor adjustments...
All set for tonight. Later, I can look at this in the light of day, and ponder whether my actions made any sense at all.
Good night.