This might be a better way of doing types than annotate. It's quite similar to the way Mathematica works, where all objects are expressions of the form <type a b c>. Even lists are like this: <list 1 2 3>. If the 'type' is a function then the form gets evaluated, just like a list in Lisp.
Well, this approach basically is the 'annotate approach, except instead of having a separate basic data type - a "tagged" object - you just use cons cells, where the car is the type and the cdr the rep.
The problem is that unless you make lists also use this representation, as Mathematica does, you can't distinguish between lists and objects of other types - a major problem, especially if you use anarki stuff like 'defm and 'defcall. Moreover, objects of this form do not evaluate to themselves, but to structurally equivalent objects. This is usually okay, but if you're using shared, mutable data structures, it's kind of problematic; moreover, it can introduce a lot of unnecessary consing. This isn't likely to be a major problem (how often are objects subject to excess evaluation?), but if it does crop up, it could make for some nasty bugs.
> Well, this approach basically is the 'annotate approach, except instead of having a separate basic data type - a "tagged" object - you just use cons cells, where the car is the type and the cdr the rep.
Except for the case where (isa R T), where (annotate T R) returns R directly:
This isn't a fundamental difference. Just as 'annotate could as easily create a new doubly-wrapped object, 'annotate-cons can as easily not cons when the type is the same.
(def annotate-cons (typ val)
(if (isa val typ) val (cons typ val)))