Consider the following Abstract Data Type (using Haskell syntax):
data Expr = Literal String | Symbol String | And [Expr] | Or [Expr]
In Python, one can make use of dataclasses and inheritance to obtain a similar type construction:
@dataclass
class Expr:
# "union"
foo: str | None = None
bar: list["Expr"] | None = None
@dataclass
class Literal(Expr):
pass
@dataclass
class Symbol(Expr):
pass
@dataclass
class And(Expr):
pass
@dataclass
class Or(Expr):
pass
As an interesting exercise, I am wondering whether it is possible to obtain a similar effect, but with a different definition which avoids duplication. I came up with the following theoretical notation:
# simulate Haskell's
# data Expr = Literal String | Symbol String | And [Expr] | Or [Expr]
# the following will bring names into scope
(
make_adt(
type_cons="Expr",
possible_fields=[
("foo", str),
("bar", list["Expr"]),
],
)
.add_data_cons("Literal", fields=["foo"])
.add_data_cons("Symbol", fields=["foo"])
.add_data_cons("And", fields=["bar"])
.add_data_cons("Or", fields=["bar"])
)
Here I am saying that there's a base type (the Expr
type constructor) with 4 data constructors: Literal
, Symbol
, And
, Or
.
Each data constructor takes an additional argument (either str
or list[Expr]
), which is referred in the fields
argument above (must be a subset of the possible_fields
).
So:
Literal("foo")
: sets thefoo
field for the instanceAnd([Literal("foo"), Symbol("baz")])
: sets thebar
field for the instance
The constraint here, as opposed to plain inheritance, is that that Literal
and Symbol
don't have the bar
field, and similarly, And
, Or
, don't have the foo
field. Or, to relax this a bit, we at least have to enforce that only non-null attributes are the ones defined in the fields
list above.
My questions are:
- Can something like this be implemented?
- I'm thinking along the lines of attrs and dynamic
class
creation usingtype(...)
.
- I'm thinking along the lines of attrs and dynamic
- How brittle it would be?
P.S. I know it does not necessarily make sense to over-engineer this, especially in a dynamically typed language like Python, but I consider it to be an interesting exercise nonetheless.