Coding 2023-04-07

Tags:
By Max Woerner Chase

I thought some about what I want from the other decorator. Something like...

# parametric.py

def make_deco(
    converter: Callable[[Any], Paramatric[object]
) -> Callable[[Callable[..., T]], Parametric[T]]:
    def deco(function: Callable[..., T]) -> Parametric[T]:
        signature = inspect.signature(function)
        instantiators = {}
        metadata = Metadata()
        for param in signature.parameters.values():
            default = converter(param.default)
            metadata = metadata.combine(default.metadata)
            instantiators[param.name] = default.instantiator

        def instantiate(box, selections):
            kwargs = {}
            for name, instantiator in instantiators.values():
                kwargs[name] = yield from instantiator(box, selections)
            return function(**kwargs)

        return Parametric(instantiate, metadata)

    return deco

@make_deco
def parametric(default: Any) -> Parametric[object]:
    if not isinstance(default, FakeValue):
        raise ValueError
    return default.parametric

# another module
def adaptive(function: Callable[..., T]) -> AdaptiveParametric[T]:
    segment_parametrics = []

    @_parametric.make_deco
    def deco(default: Any) -> Parametric[object]:
        if not isinstance(default, SomeOtherFakeValue):
            raise ValueError
        mapped_parametric = default.value
        parametric = mapped_parametric.parametric
        segment_parametrics.append(
            _parametric.map_over_1_parametric(
                mapped_parametric.map, parametric
            )
        )
        return parametric

    parametric = deco(function)

    return AdaptiveParametric(tuple(segment_parametrics), parametric)

It might look a little weird that I'm using the old mapping functions as part of implementing the new mapping functions, but the thing is, there are places where I feel like it doesn't make sense to switch from the old system, so I might as well keep it, and if I'm keeping it, I might as well use it in new places as well.

Aside from the corrections to obvious mistakes I made last-time-ish, I'm now considering what I want the new MappedParametric concept to look like. The "problem" is, both of the things that go into it are functions, unlike the old way, which used a label instead of a parametric. I can't think of which one I'd want to have set up in a decorator position, so maybe... both?

def mapped_parametric(function=None, /, *, map=None, parametric=None):
    none_count = (function, map, parametric).count(None)
    if none_count == 0:
        raise ValueError
    if none_count == 1:
        return MappedParametric(
            map=map or function, parametric=parametric or function
        )
    if function is not None:
        raise ValueError
    return partial(mapped_parametric, map=map, parametric=parametric)

That seems a little cursed, so it would be better to inventory how this would actually be used, before actually putting this in the code and trying to annotate it. (A more restrained way to accomplish something similar would be to create alternative constructors with classmethod, but that's not as funny as putting cartoon swearing in the parameter list.)

Anyway, that's enough for now. I'm going to try to figure out what happened to last night's entry, then wind down.

Good night.