Arc Forumnew | comments | leaders | submitlogin
Macro trouble
3 points by schtog 5846 days ago | 10 comments
arc> (mac meta(x . y) (+ x y)) * redefining meta #3(tagged mac #<procedure>) arc> (meta 1 2) Error: "+: expects type <number> as 2nd argument, given: (2); other arguments were: 1" arc> (meta '1 '2) 1 arc> (eval (+ '1 '2)) 3

why is (meta '1 '2) returning 1? why doesnt (meta 1 2) work?



3 points by skenney26 5846 days ago | link

If all you want is to add two numbers together, a function will be sufficient:

  (def meta (x y)
    (+ x y))
Macros are usually only used when trying to accomplish something that cant be done with a regular function. If you want to accept an arbitrary number of arguments, the following would work:

  (def meta (x . y)
    (apply + x y))
or:

  (def meta xs
    (apply + xs))
This could also be accomplished with a macro:

  (mac meta xs
    `(+ ,@xs))
In your original definition of meta, arc thinks that y is a list since it follows the dot syntax (x . y) It then throws an error when it evaluates (+ 1 (2)) because it doesn't know how to add a number to a list.

-----

1 point by schtog 5846 days ago | link

i was just playing around with macros figuring out more precisely how they work.

  arc> (mac meta xs
      `(pr ,@xs))
* redefining meta #3(tagged mac #<procedure>) arc> (meta 3 3 4 5 66 67) 334566673

why is this macro returning the first number in the end?

and why is the @ there? leaving it otu gives the same result for the tests i made.

-----

3 points by skenney26 5846 days ago | link

It looks like pr prints out all its arguments and then returns its first argument. You can safely ignore the final value. If pr was used in a web page to print out some text, the return value would never be seen.

As far as the @ symbol, the following code causes an error:

  arc> (mac meta xs
         `(+ ,xs))
  *** redefining meta
  #3(tagged mac #<procedure>)
  arc> (meta 1 2)
  Error: "Function call on inappropriate object 1 (2)"
The comma symbol inserts a value:

  arc> (= y '(1 2 3))
  (1 2 3)
  arc> `(x ,y z)
  (x (1 2 3) z)
,@ inserts a value and removes the outermost pair of parens:

  arc> `(x ,@y z)
  (x 1 2 3 z)

-----

1 point by jmatt 5846 days ago | link

xs will have a type of 'cons in this situation. Which means it sees it as (element . list). So for your example it'll see it as 3 (3 4 5 66 67). It just happens that pr deals with cons types well.

If you wanted to return another value, you could.

  (mac meta xs `(do (pr ,@xs) (last ',xs)))

  arc> (macex '(meta 1 2 3 4 5 6 7))
  ((fn () (pr 1 2 3 4 5 6 7) (last (list 1 2 3 4 5 6 7))))

  arc> (meta 1 2 3 4 5 6 7)
  12345677
Another way to return the last element of fn...

  (mac meta xs `(do (pr ,@xs) ,@xs))
  
  arc> (macex '(meta 1 2 3 4 5 6 7))
  ((fn () (pr 1 2 3 4 5 6 7) 1 2 3 4 5 6 7))
  
  arc> (meta 1 2 3 4 5 6 7)
  12345677

-----

3 points by jmatt 5846 days ago | link

I think what you want is this:

  (mac meta(x . y) (+ x (car y)))
The way . works in parameters is that it is a list of the rest of the parameters that you are passing to the function/macro. This list could be empty/nil or contain one or more elements.

  arc> (mac meta (x . y) (prn x y))
  1(2)
  1
  arc> (meta 1)
  1()
  1
  arc> (is nil '())
  t
Don't forget that nil and the empty list are equivalent in arc.

EDIT: Added some explanation.

-----

2 points by croach 5846 days ago | link

schtog,

jmatt explained why you are having trouble with the second parameter, essentially its a list and not a number like the '+' function is expecting. You could write your macro this way to expand the rest list into individual parameters to the '+' function:

  (mac meta (x . y) 
    `(+ ,x ,@y))
I think this macro works a bit more like you are expecting it to.

-----

1 point by schtog 5846 days ago | link

  (mac meta (x . y) 
      `(pr ,x ,@y))
arc> (meta 1 2 3 4 5) 123451

still dont see why the 1 is repeated in the end.

-----

2 points by sacado 5846 days ago | link

Everything returns a value in arc, at least nil, or any other value. When you evaluate something through the REPL, the result is displayed. When you call 'pr, all the args are displayed, then the first arg is returned (then, displayed by the REPL). If you type, say

  (for i 1 10 (pr 1 2 3 4 5))
You will not see the 1 repeated on each iteration. It will print nil, though : that's the value returned by 'for.

-----

1 point by jmatt 5846 days ago | link

Check out the definition for pr on arcfn. http://www.arcfn.com/doc/io.html

Prints arguments using disp. Returns the first argument.

-----

1 point by tokipin 5845 days ago | link

here's a macro that prints them out more telligibly:

  (mac pr2 args
       (let l (len args)
         `(do
            (pr ,@(firstn (- l 1) args))
            ,(last args))))

-----