Arc Forumnew | comments | leaders | submitlogin
1 point by shader 5549 days ago | link | parent

Why do you need the eval in rdefop? What am I missing?


1 point by thaddeus 5549 days ago | link

I am not sure. It didn't work for me when I took it out.

-----

3 points by fallintothis 5548 days ago | link

You don't really need eval in the macro. In the original,

  (each r routes*
    (rdefop r))
macroexpands into

  (each r routes*
    (defop r req (pr "Schedule for " r " goes here")))
without evaluating what r is bound to --- you'll just get an /r handler.

So, using eval makes sure r gets evaluated. But you do this haphazardly between two points:

  (mac rdefop (rs)
    (eval `'(defop ,(fromstring  rs (read)) req
                  (rpage ,rs))))

  (each r routes*
    (eval `(rdefop ,r)))
We only need to eval once.

  (= routes* '(10a 10b 15a 20a 20b 30a 30b 30c 40a 59u))

  (def rpage (r)
    (pr r))

  (mac rdefop (r)
    `(defop ,r req (rpage ',r)))

  (each r routes*
    (eval `(rdefop ,r)))
Thus, instead of doing (defop r), it's kind of like we're doing (defop (eval r)). That is, instead of expanding into

  (defop r req (rpage 'r))
we're expanding into

  (defop 10a req (rpage '10a)) ; when r == '10a
  (defop 10b req (rpage '10b)) ; when r == '10b
  ; etc
We could've done the eval inside the macro, too, but that's often a sign you're doing something wrong --- macros are usually there to not evaluate their arguments. So we should probably use a function.

However, defop itself is a macro, so the first parameter (the op's name) won't be evaluated regardless. We still need eval.

  (= routes* '(10a 10b 15a 20a 20b 30a 30b 30c 40a 59u))

  (def rpage (r)
    (pr r))

  (def rdefop (r)
    (eval `(defop ,r req (rpage ',r))))

  (each r routes*
    (rdefop r))
Since this approach seems to require eval regardless, we should just look for a better solution. aw's works nicely.

Some other nitpicks over your rewrite (hey, you asked!):

1) Unless you need strings for some reason, you can probably default to symbols (they're a bit easier to use).

  ; Instead of

  (= routes* '("10a" "10b" "15a" "20a" "20b" "30a" "30b" "30c" "40a" "59u"))

  ; we could use

  (= routes* '(10a 10b 15a 20a 20b 30a 30b 30c 40a 59u))
2) Proper spacing & indentation saves lives. :)

  ; Instead of

  (defop shuttle req
    (each r routes* (link r)(nbsp)))

  ; why not

  (defop shuttle req
    (each r routes* (link r) (nbsp)))

  ; or even

  (defop shuttle req
    (each r routes*
      (link r)
      (nbsp)))
3) Though using symbols renders this point moot, fromstring is unnecessary to simply (read) from a string, since Arc's definition of read is

  (def read ((o x (stdin)) (o eof nil))
    (if (isa x 'string) (readstring1 x eof) (sread x eof)))
So, instead of

  (fromstring rs (read))
you can use

  (read rs)
If your goal is just to turn a string into a symbol, you should use

  (sym rs)
This is an important distinction. e.g.,

  arc> (sym "abc")
  abc
  arc> (read "abc")
  abc
  arc> (sym "(a b c)")
  |(a b c)|
  arc> (type that)
  sym
  arc> (read "(a b c)")
  (a b c)
  arc> (type that)
  cons

-----

1 point by evanrmurphy 5548 days ago | link

Very instructive. Thank you for doing such a thorough analysis.

',r from your snippet

  (mac rdefop (r)
    `(defop ,r req (rpage ',r)))
was a realization for me. Never thought of quoting a comma'd symbol in a backquoted expression before, but I like knowing it's possible. Do you find yourself doing this much, or is there usually something simpler like aw's solution available to make it unnecessary?

-----

2 points by fallintothis 5548 days ago | link

Happy to help.

Oh, yes, quoted-unquotes are done pretty often.

  $ grep -c "'," {arc,code,html,srv,app,news}.arc
  arc.arc:17
  code.arc:1
  html.arc:6
  srv.arc:9
  app.arc:1
  news.arc:7
For example, the Arc built-in obj is a macro that makes a hash table with automatically-quoted keys.

  arc> (= h (obj a 1 b 2))
  #hash((b . 2) (a . 1))
  arc> (h 'a)
  1
  arc> (h 'b)
  2
It's defined like so:

  (mac obj args
    `(listtab (list ,@(map (fn ((k v))
                             `(list ',k ,v)) ; note we unquote k, then quote it
                                             ; so we're quoting the value of k
                           (pair args)))))
That way, the above (obj a 1 b 2) expands into

  (listtab (list (list 'a 1) (list 'b 2)))

-----

1 point by evanrmurphy 5548 days ago | link

Thanks again.

-----

1 point by thaddeus 5548 days ago | link

dito

-----