Coding 2023-04-05
Okay, the magma is written, and I'll verify that it's "actually correct in general" later, hopefully after I've nailed down some requirements for it.
Let's start working on the next parametric overhaul.
The first half of this has several parts:
- A wrapper class around Parametric objects.
- A property on Parametric that is documented to return an object of the underlying type, but actually returns an instance of this wrapper class.
- A decorator that takes the signature of the function it's passed, and makes sure that all arguments have a default value that's an instance of the wrapper class. Then, map over all of them.
It's probably worth calling the decorator just "parametric". The type should be called, um... FakeValue, or something. Something that hopefully makes it really clear that it shouldn't work for most purposes. And the property, just value, maybe?
I don't know if I want to put these ideas into the actual module yet, but let's see what I can do in code blocks here.
# Eventually, these will be replaced with "attrs.frozen"
@attr.dataclass(frozen=True)
class FakeValue(typing.Generic[T_co]):
parametric: Parametric[T_co]
...
class Parametric(typing.Generic[T_co]):
...
@property
def value(self) -> T_co:
return typing.cast(T_co, FakeValue(self))
def parametric(function):
signature = inspect.signature(function)
instantiators = {}
metadata = Metadata()
for param in signature.parameters.values():
default = param.default
if not isinstance(default, FakeValue):
raise ValueError
metadata = metadata.combine(default.metadata)
instantiators[param.name] = default.instantiator
def instantiate(box, selectors):
# I'm pretty sure Python is too strict to allow this, but eh.
kwargs = {name: (yield from instantiator(box, selectors)) for name, instantiator in instantiators.values()}
return function(**kwargs)
return Parametric(instantiate, metadata)
I hope this basically works, disregarding the syntactic gunk that I'll have to work around. I'm going to wrap things up for now, and think about it.
Good night.