Arc Forumnew | comments | leaders | submitlogin
Arc's defset : a little anti-arc?
3 points by cooldude127 5968 days ago | 7 comments
Is there a reason that Arc's defset is more complex than CL's defun (setf ...)? It seems like Arc's philosophy of shorter programs would benefit from simpler setter definitions. Here is an example:


  (defun (setf pval) (val puz x y)
    (setf (aref puz x y) val))

  (defset pval (puz x y)
    (w/uniq (gpuz gx gy)
      (list (list gx x
                  gy y
                  gpuz puz)
            `(pval ,gpuz ,gx ,gy)
            `(fn (val) (= ((,gpuz ,gy) ,gx) val)))))
Unless I'm missing something, there is no good reason it should be this complex.

4 points by pau 5968 days ago | link

Arc's 'defset' is similar to CL's 'get-setf-method'. These functions return a list of information to get or set a 'generalized variable'. When the macro '=' is invoked, it uses this information to generate the code. I guess you could write something like CL's 'defsetf' in Arc (maybe 'def='?). In CLTL2 Common Lisp, this 'defsetf' can be expressed as (defun (setf ...) ...), which seems more natural.

Take a look at PG's book 'On Lisp', chapter 12, pages 171 and 178...


3 points by cooldude127 5967 days ago | link

Here is a macro called def= that will do basically what the equivalent CL defun would:

  (def fix-body (gens args body)
    (let genargs (map list args gens)
      (if (alist body)
          (map [fix-body gens args _] body)
          (find body args)
          (list 'unquote (alref genargs body))

  (mac def= (name (val . args) . body)
    (let gens (map [uniq] args)
      (list 'defset name args
            (list 'w/uniq gens
                  (list 'list
                        (cons 'list
                              (flat (map list gens args)))
                        `(list ',name ,@gens)
                        (list 'quasiquote (join (list 'fn (list val))
                                                (fix-body gens args body))))))))
Here is an example definition for a caddr setter:

  (def= caddr (val xs)
    (= (car (cdr (cdr xs))) val))
Any improvements are welcome.


2 points by kennytilton 5968 days ago | link

So write defun the Arc macro, the symbol seems to be available. Have it accept (= <??>) as the first param and expand into the defset boilerplate. And it is boilerplate, I simplified arccells client code by cutting/pasting the defset I found for car. Don't forget optional and keyword args. :)

The larger point is that it is exceedingly rare to code a (setf ???) form in CL, tho of course when you want it it totally rocks. But in Arc or CL when I need to get the hood up to do some brown belt coding I expect some unusual wiring to appear.


2 points by cooldude127 5968 days ago | link

I think I will, when I have the time, write a macro in Arc to simplify this.

I'm surprised you say that writing a setf in CL is rare. Perhaps because usually CLOS is doing the work for you?


1 point by kennytilton 5968 days ago | link

Oh, it is not just CLOS. defstruct also defines setters and many an apparent reader function is also a writer, (so we call them accessors). That means one does not fire up (defun (setf foo)...) unless there is an usual case of abstraction where there is some function on an object (not just reading a slot) and where there is some state change intelligibly viewed as being transitive aka share the name with the reader.

In re writing a defun, I probably just talked you out of it, but here is what I did (warning, old nooby Arc code) for optional and keyword args:

  (mac defun (name params . body)
   (w/uniq (rtargs)
    `(def ,name ,rtargs
       (withs ,(with (reqs nil key? nil opt? nil keys nil opts nil without)
                (each p params
                  (if (is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
                                     (assert (no key?) "&k cannot precede &o:" ',params)
                                   (= opt? t))
                    (is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
                                   (= key? t))
                    key? (push-end p keys)
                    opt? (push-end p opts)
                    (do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
                        (push-end p reqs))))
                (with (n -1 kvs (uniq))
                  (+ (mappend [list _ `(nth ,(++ n) ,rtargs)] reqs)
                    (mappend [list (carif _) `(or (nth ,(++ n) ,rtargs)
                                                ,(cadrif _))] opts)
                    (list kvs `(pair (nthcdr ,(++ n) ,rtargs)))
                    (mappend [list (carif _)
                               `(or (alref ,kvs ',(carif _))
                                       ,(cadrif _))] keys)


7 points by cooldude127 5968 days ago | link

also, i find myself not using clos or defstruct all that much in my cl code. i tend to use ordinary data structures like lists and arrays.

my recent example (an excerpt from which i already showed) was a sudoku solver. i wrote one version in CL, then thought i'd do it in arc for fun. in CL, the puzzles were simply arrays (in Arc, they were lists do to the lack of an array type).

However, i didn't want my code to use the array (or list) accessors built into the language to access values in the puzzle, because that is not abstract enough. so i defined a function called pval that takes the puzzle and an x and y, giving the value in that square. that way, my code deals with puzzles, not arrays.

Then I wanted to be able to set the value in a square using the same pval accessor. Trivial in CL, but Arc made it a little difficult. This comes up rather often for me.

That point might not have justified such a long explanation. Whoops.


2 points by cooldude127 5968 days ago | link

all i really intend to do is a simple def= macro that will transform (def= pval (val puz x y) (= ((puz y) x) val)) into the previously given defset form. shouldn't be too bad. i have no intentions of mimicing CL's defun. i honestly like arc's def as it is.