Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Enough metaprogramming for dataclass

In SPy roadmap, there is the TODO item “enough metaprogramming capabilities to implement e.g. dataclasses”.

Here is a reasonable minimal dataclass implementation in future SPy:


from operator import OpSpec
from __spy__ import unroll_range

@blue.generic
def dataclass(cls):
    @blue
    def get_fields():
        # needs: read cls's annotated body as an ordered {name: type} dict,
        # the way @struct itself already must do internally for a normal
        # `field: T` class body. Not yet exposed to user-level blue code
        # in any of the examples above.
        return cls.__annotations__

    fields = get_fields()
    names = (*fields.keys(),)
    types = (*fields.values(),)
    N = len(names)

    @struct
    class _dc:
        __extra_fields__ = fields

        @blue.metafunc
        def __new__(m_self, *args_m):
            arg_types = args_m.types
            # could raise TypeError here if arg_types != types

            def new(*args: *arg_types) -> _dc:
                return _dc.__make__(*args)

            return OpSpec(impl, [*args_m])

        @blue.metafunc
        def __eq__(m_self, m_other):
            def impl(self: _dc, other: _dc) -> bool:
                ok = True
                for i in unroll_range(N):
                    if getattr(self, names[i]) != getattr(other, names[i]):
                        ok = False
                return ok
            return OpSpec(impl, [m_self, m_other])

        @blue.metafunc
        def __repr__(m_self):
            def impl(self: _dc) -> str:
                s = "Point("
                for i in unroll_range(N):
                    if i > 0:
                        s = s + ", "
                    s = s + names[i] + "=" + repr(getattr(self, names[i]))
                return s + ")"
            return OpSpec(impl, [m_self])

    return _dc

@dataclass
class Point:
    x: f64
    y: f64

List of features to be implemented

Different things are currently not implemented.

unroll for loops

This is the most complicated feature and Antonio is working on it (September-October 2026).

cls.__annotations__ (or equivalent)

This should be a small addition.

fields.key() and fields.values()

Note that fields is a dict[str, type], which is implemented as a interp_dict[str, type].

But “method __spy__::interp_dict[str, type]::keys does not exist”.

(*fields.keys(),) and OpSpec(impl, [*args_m]) in blue functions

Being implemented in spylang/spy#710

*args: *args_m.types for red functions arguments

args would be a tuple[*args_m.types] (PEP 646).

unroll_range as a splice generator

This is a direct follow up of unroll for loops.

*(args[i] for i in unroll_range(len(args))) could be redshifted as args[0], args[1], ...

Once we have unroll for loops, the implementation should not be very difficult.

Call with starred (func(*args)) in red functions

func(*args) could then be syntactic sugar for func(*(args[i] for i in unroll_range(len(args)))).