def constraint_domain(node: cl.ObjectSetExpression, finalize_variables=False) -> Domain:
    """Given an expression, find a compact representation of what types of objects it is applying to.

    User can compared the resulting Domain against their State and see what objects fit.
    """

    assert isinstance(node, cl.ObjectSetExpression), node

    recurse = partial(constraint_domain, finalize_variables=finalize_variables)

    match node:
        case cl.tagged(objs, tags):
            d = recurse(objs)
            d.tags.update(tags)
            if t.contradiction(d.tags):
                raise ValueError(
                    f"Contradictory tags {tags=} for {d=} while parsing constraint {node=}"
                )
            return d
        case cl.related_to(children, parents, relation):
            c_d = recurse(children)
            p_d = recurse(parents)
            c_d.add_relation(relation, p_d)
            return c_d
        case cl.scene():
            return Domain()
        case cl.item(x):
            if finalize_variables:
                return recurse(
                    node.member_of
                )  # TODO - worried about infinite recursion somehow
            else:
                return Domain(tags={t.Variable(x)})
        case FilterByDomain(objs, filter):
            return filter.intersection(recurse(objs))
        case _:
            raise NotImplementedError(node)
Given an expression, find a compact representation of what types of objects it is applying to.

    User can compared the resulting Domain against their State and see what objects fit.
    
infinigen/core/constraints/reasoning/constraint_domain.py