I actually used "obj" as a reference when writing "new", which is why they were so similar. I decided not to use a gensym because I figured that "this" would effectively be a reserved word with a common meaning and so that I could use "this" in my methods. However, your point about clashes is well taken.
As for "defm", it looks pretty nice. I guess I wouldn't mind calling "(until p 30)" instead of "(p!until 30)" so long as I don't have to call "person-until" just to prevent naming clashes.
I really dislike having to call "((rep self) 'first)" instead of "self!first", but as you point out, I might be able to define some macros to make this less verbose.
I guess for now I'll start playing around with Anarki. Hopefully the more knowledgable Lispers out there will settle on an object system that's general enough to be useful and concise enough to by enjoyable.
Ah, I see -- new is an auto-binding obj. That makes sense if you're including methods (which perhaps you shouldn't). It's just that usually when people do that it is a mistake :)
Actually, that was a mistake on my part; you can in fact write
(defm full ((t self person))
(string self!first " " self!last)
(defm until ((t self person) year)
(- year self!age)
because the defcall allows you to put objects of type person in that position. And now the only redundancy left is having to write (t self person) all the time, which you may or may not want to abstract.
The other missing feature is the inability to say (= p!age 42); for that, you need to override sref:
; Insert error checking so that you can't
; write (= p!species 'banana).
(defm sref ((t self person) value key)
(= ((rep self) key) value))
Again, <plug>the tagged unions do all of this for you (except for defining your own methods, which you have to do with defm and vcase/tcase)</plug>. Of course, they don't have inheritance.
That sounds excellent, and it makes me a lot more excited about using objects in Arc. I don't care that much about having to write (t self person) a lot, since it seems like an acceptably low amount of boilerplate.
However, out of curiosity, why do you have to write "t" instead of just saying "(self person)"? I read through the code for defm and argmap, but my Lisp reading skills aren't strong enough to decipher it.
Because parameter lists in Arc support destructuring. What that means is that anywhere you can write variable to bind a name to a value (such as in (let variable 10 (prn variable))), you can also write (v1 v2) to bind a list's first element to v1 and second element to v2. And (o v default) denotes optional parameters. Perhaps some examples would be clearer:
(with (i 1 v 5 x 10)
(let a (list i v) (prn "a is (1 5)."))
(let (b c) (list i v) (prn "b is 1 and c is 5."))
(let (d . e) (list i v x) (prn "d is 1 and e is (5 10)."))
(let (f (o g)) (list i) (prn "f is 1 and g is nil."))
(let (h (o j 42)) (list i) (prn "h is 1 and j is 42."))
(let (k (o m)) (list i v) (prn "k is 1 and m is 5."))
(let (n (o p 42)) (list i v) (prn "n is 1 and p is 5.")))
defm adds a (t var type) syntax; if you left out the t, you would have ordinary destructuring bind.