Convert ProgrammableRotationGateArray to an attrs dataclass
Every other bloq is an attrs dataclass, which makes it easier to inspect its classical compile-time parameters (attributes).
Apparently you can make a metaclass for Bloq that automatically makes all subclasses frozen attrs dataclasses (well, here only tested for regular dataclasses, but I'm sure attrs works the same way), which might be good for ensuring consistency. Do you think it'd be worth doing, or too much magic?
@dataclass_transform()
class DataclassABCMeta(ABCMeta):
def __new__(metacls, name, bases, namespace, **kwargs):
cls = super().__new__(metacls, name, bases, namespace)
if "__dataclass_fields__" not in cls.__dict__:
cls = dataclass(cls, frozen=True)
return cls
(The @dataclass_transform() decorator on the metaclass allows vscode and pycharm to recognize all the subclasses as attrs classes, so they'll show the constructor params and autocomplete object properties).
Actually that might not be a good option since lots of bloqs also inherit from cirq.Gate, which has its own metaclass.
Another possibility would be to have a @bloq decorator that applies attrs.frozen to the class plus whatever else might be useful at the decorator level. A possible benefit there is that you'd be able to add/change decorator-level functionality there and it would apply everywhere. The main weird thing about that approach is that bloq classes would both have to inherit from Bloq and have the @bloq decorator.
I can't think of anything else a @bloq decorator would be useful for beyond just ensuring that all bloqs are using @attrs.frozen instead of relying on convention, but maybe others might have good ideas. Or maybe relying on convention is good enough.
A lot of tooling (pycharm, mypy) has hard-coded special casing specifically for the @attrs decorator(s), so we can't use our own decorators if we want the attrs syntax (which we do)
I don't think it needs to be a hard requirement for all bloqs (including user code) to be an attrs dataclass; but maybe things like serialization won't work if it's not
The main reason I bring it up is that it'd be nice if attrs.evolve (the equivalent of dataclasses.replace) could be safely assumed to work for all bloqs. Like if you wanted a readonly Bloq.tags field, it'd be nice to be able to call attrs.evolve(bloq, tags=new_tags) on any bloq and be sure it'll succeed and return the same bloq type. Otherwise you end up having to either create a wrapper class and get into the TaggedOperation mess from Cirq, or create a Bloq.with_tags() abstract method that all subclasses have to implement.
The latter might not be too bad since you could provide a default implementation that delegates to attrs.evolve, and then only non-attrs-based bloqs would have to override it. But still, it'd be better if everything was an attrs class a-priori and users didn't have to think about it at all.
A lot of tooling (pycharm, mypy) has hard-coded special casing specifically for the @attrs decorator(s), so we can't use our own decorators if we want the attrs syntax (which we do)
Adding @dataclass_transform() to a metaclass or a decorator definition does this. Any classes that have been decorated by a decorator that's been decorated with @dataclass_transform() will appear as though they've been directly decorated by @dataclass in editors and typecheckers. So the following will type check and appear as a dataclass/attrs definition in current versions of pycharm and vscode.
@dataclass_transform()
def foo(cls):
return dataclass(cls)
@foo
class Bar:
i: int
bar = Bar(3)
print(bar.i)
All that said, maybe there are cases where users want to be able to define bloqs that aren't attrs based. Multiple inheritance from non-attrs classes that have their own fields and __init__ methods might be tricky to make work nicely with attrs, for instance. Anyway, primarily I just wanted to point out how enforcing attrs would let qualtran avoid the TaggedOperation annoyances that users of cirq get tripped up on, but if the ability to support non-attrs based bloqs is needed, then that settles that.
Actually there's a built-in copy.replace() now, that works by calling into a user-defined __replace__ magic method. So that could be an easy fallback. If there's ever a tags feature, then bloqs have to either use an attrs class (preferred) or implement __replace__ to be taggable.
https://docs.python.org/3/library/copy.html#copy.replace
I don't anticipate supporting "tags" any time soon. The recommendation is to use class attributes for compile-time classical parameters to configure your bloqs