The major difference between my version and yours is that you require each constructor to belong to one "union" type, whereas in my version each constructor forms its own type. The latter approach seems more Arc-like to me. It's simpler, and in a dynamic language I don't think you should be forced to stuff constructors into fixed categories. (I'm not saying that such categories are useless, just that variant tags shouldn't be inextricably tied to them.)
You may want to look at OCaml's polymorphic variants, which are closer to what I had in mind for Arc:
The auto-binding of names to fields seems cool, but I'd need to practice it to see if it was a good strategy overall. You've already managed to shadow car and cdr, which could cause a lot of code to fail mysteriously (think macroexpansions, e.g. each). Also, if the user binds the names explicitly, you get destructuring for free.
Sorry if this is incoherent; I've been up all night.
I remember that discussion; in fact, I had a response, http://arclanguage.org/item?id=3518, in which I proposed this (and I might have been working on this at the time).
The clearest advantage of distinct types is in things like binary trees:
you can't quickly tell what's a binary tree and what isn't in the same way, and you lose the invariant that a branch's left and right are both binary trees. At the same time, something more like vtype would remove
And you should be able to write a macro that will convert something nicer to that form, e.g.
(vcase var
variant1 (do-something var)
variant2 (do-something-else var)
(fail))
. I'll work on that and push it to Anarki.
I'm not convinced on the auto-binding; I implemented it that way because the resulting syntax was nice. It might make sense to have an option to work without auto-binding of names, but maybe it won't actually be an issue in practice. car and cdr are a bit atypical because (I hope) you won't be redefining a list type :)
I feel like there ought to be a better way to do this--something that's less complex but handles more cases. In particular I'd like to be able to alternate OO and pattern-matching styles on the same datatypes. However, I haven't been able to come up with a solution that really satisfies me.
One other thing: wouldn't it be better to raise an error when an invariant is violated, instead of returning nil?
You can use OO structure-access semantics with tagged unions: (tu 'slot-name) and (= (tu 'slot-name) new-value). That's not the interesting part of OO, of course :)
I thought about returning an error, but Arc seems to be a little calmer about such things: (is (car nil) (cdr nil) nil), for instance. Of course, (car 'sym) errors. ::shrug:: I'm not sure about this, and it might change.
The purpose of a type system is to help find errors by failing early instead of letting bugs propagate through a system and show up in mysterious ways. Right now, you're actually making debugging harder because you're just wrapping bugs inside other bugs. Instead of figuring out why his binary tree turned into a cheeseburger, a programmer now has to determine (a) why his binary tree is nil (or why mutating it seems to have no effect), (b) which invariant he violated, and (c) what error caused the invariant to be violated.
Regarding (car nil) and (cdr nil), these return nil because it's sometimes convenient. For instance, here's a function that collects every other item in a list:
EDIT: Creation, reading (defcall), and writing (sref) all give errors if invariants are violated/nonexistent keys are accessed. Is there anything I missed?