Update: I (think I)[1] added rkts's suggestion of "solitary" tagged unions/variants, which act very similarly to plain structures except with the availability of a vcase macro (very much like tcase). They are defined with
(vtype name (arg1 pred1) arg2 ... (argN predN))
, where an argument without a predicate is implicitly (arg object) (you can't do this in tcase without extra parentheses, though). vcase works similarly to tcase:
(vcase var
name1 (expr1)
name2 (expr2)
...
nameN (exprN)
(optional-error-condition))
Note that unlike in tcase, the names aren't connected, but are merely defined by whichever vcase (or a tcase with a variant named the same thing as the union, which is what a vcase type is).
This required fixing a bug where you couldn't have a variant with the same name as its tagged union (because you then had (annotate NAME (annotate NAME ...)), and that is equivalent to (annotate NAME ...)). This means that you can do that now, but that *(~isa (rep tu) 'variant-type)&.
[1]: That is, I added something which I think is what rkts suggested; rkts will probably disabuse me of this notion rather quickly if I got something wrong :)
Yes, but. That only holds if in (annotate t1 (annotate t2 data)), we have (isnt t1 t2) (there was a thread about this somewhere, but I can't find it). Try it!
arc> (annotate 'foo (annotate 'bar obj))
#3(tagged foo #3(tagged bar #3(tagged mac #<procedure>)))
arc> (annotate 'foo obj)
#3(tagged foo #3(tagged mac #<procedure>))
arc> (annotate 'foo (annotate 'foo obj))
#3(tagged foo #3(tagged mac #<procedure>))
I was surprised too; that's why this was a bug I had to fix. I suppose this behaviour makes sense, but it does break the second of the two identities
(is (type (annotate t r)) t)
(is (rep (annotate t r)) r)
Huh. Have you tried it on PG's ArcN? It might have been me hacking this onto Anarki (I have multiple personalities. No, don't believe what I just said, that wasn't me. LOL). Don't have access to an arc right now, sorry ^_^