Sketch of how to solve this kind of problem for all syntactically tricky
.WHICH cases:
class ObjAt {
method make(*@items) {
self.new(@items.map({
"(" ~
(.^isa(ObjAt) ?? .Str !!
.Str.subst(/<[\(\)\!]>/,
{ sprintf("!\%d", .ord - 32) }, :g))
~ ")"
}).join(" "))
}
method WHICH(ObjAt:D:) { ObjAt.make(self.WHAT.WHICH, self) }
}
class Mu {
method WHICH(Mu:D:) { ObjAt.make(self.WHAT.WHICH, nqp::objectid(self)) }
}
class Str {
method WHICH(Str:D:) { ObjAt.make(self.WHAT.WHICH, self) }
}
class Int {
method WHICH(Int:D:) { ObjAt.make(self.WHAT.WHICH, self) }
}
class Pair {
method WHICH(Pair:D:) {
ObjAt.make(self.WHAT.WHICH, $!key.WHICH, $!value.VAR.WHICH)
}
}
class Set {
method WHICH(Set:D:) {
ObjAt.make(self.WHAT.WHICH,
%!elems.keys.sort.map({ ObjAt.new($_) }))
}
}
In summary: centralise the logic for unambiguously representing a
sequence of object identities and strings. Individual classes should
only have to present the right elements that will go into an identity,
not put it together themselves.
-zefram