Page 1 of 1

converting macro parameter name to key parameter

Posted: Sun May 31, 2009 4:34 pm
by suribe
Hi,

I'm learning common lisp and this is my first try at writing something useful. I wanted to create some functions and data structures to work with 3d objects. I started with a 3d-point like this:

Code: Select all

(defstruct 3d-point
	   (x 0)
	   (y 0)
	   (z 0))
And wanted to have some nice macros to create unit vectors. I didn't want to write 3 functions like:

Code: Select all

(defun versor-x ()
  (make-3d-point :x 1.0))
(defun versor-y ()
  (make-3d-point :y 1.0))
(defun versor-z ()
  (make-3d-point :z 1.0))
because I thought the point of having macros was not repeating functions, so I wanted something that would work more like:

Code: Select all

(versor x)
that would expand to:

Code: Select all

(make-3d-point :x 1.0)
Now, I'm having a lot of trouble getting it done. The problem is that I can't see a way to access the symbol name (the 'x' in this case) and later use it as a keyword parameter for the make-3d-point function (as ':x').
Is there a way to do something like that?

I tried:

Code: Select all

(intern (concatenate 'string ":" (symbol-name (second '(versor x)))))
|:X|
but I need :X, not |:X|, as in:

Code: Select all

(car (cons :X 1))
:X
And, in a more general sense, am I doing this the right way? Or should I do the 3 function variant? Or something else? Maybe ditch structures, or use them other way?

BTW, i'm using sbcl.

Thanks!

Re: converting macro parameter name to key parameter

Posted: Mon Jun 01, 2009 12:12 pm
by duncan
suribe wrote: Now, I'm having a lot of trouble getting it done. The problem is that I can't see a way to access the symbol name (the 'x' in this case) and later use it as a keyword parameter for the make-3d-point function (as ':x').
Is there a way to do something like that?

I tried:

Code: Select all

(intern (concatenate 'string ":" (symbol-name (second '(versor x)))))
|:X|
but I need :X, not |:X|, as in:

Code: Select all

(car (cons :X 1))
:X
And, in a more general sense, am I doing this the right way? Or should I do the 3 function variant? Or something else? Maybe ditch structures, or use them other way?!
OK, taking your points in a sort of backwards order- no, you probably shouldn't be using a macro for this. Macros are very powerful, but there's no real reason to use one here. Those three functions take up six lines. And if you want a shorthand for making unit vectors a normal function will do fine. I'm tempted to wax philosophical here, but that would certainly lead to excess typing on my part, and that wouldn't be in the spirit of your question, so I'll just say that macros should only be used to save typing when you need their unique characteristics toward that end. In this case you can write a normal function named versor that does (almost) exactly what you want this macro to do. You just need some form of conditional- there are a few you could use. Don't use macros where it is just as easy to use normal procedures. The only thing you get from a macro here is the ability to write (versor x) instead of (versor 'x) or (versor :x). Not only is that a frivolous use of macros, I find it a bit jarring... it betrays my expectations.

But, you also raise some other questions, so...

first off, some bits from the repl:

Code: Select all

(intern "x" 'keyword) =>
:|x|
:EXTERNAL

(intern "X" 'keyword) =>
:X
:EXTERNAL

(eq :x (intern "x" 'keyword)) =>
NIL

(eq :x (intern "X" 'keyword)) =>
T

(intern ":X") =>
|:X|
NIL

(intern "X" 'keyword)
:X
:EXTERNAL

(eq :x :X) =>
T

(eq (intern "x" 'keyword) (intern "X" 'keyword)) =>
NIL

(eq (intern ":X") (intern "X" 'keyword)) =>
NIL

(intern ":X" 'keyword)=>
:|:X|
NIL
This is with the defaults for the reader- the results would be different if you played with them a bit. You should read up on the way that the reader deals with case because it would make a long post by itself. CL is _not_ case-insensitive.... Also note that there's a big difference between interning a symbol in the keyword package and interning a symbol that starts with a colon in your current package (or some other package).

Anyway, if some of that is Greek to you, don't feel bad. There are some aspects of CL that are a bit difficult to follow at first. I think the problem here is that you're getting a bit ahead of yourself (and I don't mean to patronize you here- CL is a really big complicated language that takes some time to understand, and on top of that it is very different from most of the languages in common use these days.)

The thing is that there's almost always a simple way to do things in CL, as well as a complicated way. So I'd suggest sticking with the stuff you understand really thoroughly while also reading about things like the keyword package and the reader. You'll find, I think, that CL is as capable a language as most others even without the bells and whistles.

If you have specific questions about any of the stuff above feel free to ask, and I'll be happy to answer them if I can (and if I can't someone else will be able to, I'm sure), or point you at the relevant docs, or both.

Re: converting macro parameter name to key parameter

Posted: Mon Jun 01, 2009 1:10 pm
by suribe
Thanks for your answer! I'll look more deeply at the intern documentation for future reference, but stick with the 3 functions. I suppose that if it was so hard to make it work with macros (although I'm a beginner, 3 hours with no success for something so short seems too much) it's because it wasn't meant to.

Regards,

Re: converting macro parameter name to key parameter

Posted: Mon Jun 01, 2009 1:54 pm
by duncan
suribe wrote:Thanks for your answer! I'll look more deeply at the intern documentation for future reference, but stick with the 3 functions. I suppose that if it was so hard to make it work with macros (although I'm a beginner, 3 hours with no success for something so short seems too much) it's because it wasn't meant to.
Well your problem here is not so much a problem with macros as it is a problem with packages and the reader. I'd really recommend that you look through the snippets from the REPL I posted above, and try to figure out what they mean- they are a pretty complete explanation, I think. And those results might even be required by the standard ;).

But the main point there is about how the reader decides which package to intern a symbol in. This is worth understanding, so I'll explain it a bit better. When you type in a colon followed by a symbol the reader treats that as a symbol in the keyword package (and interns it there, possibly.) When you print a symbol from the keyword package, it gets printed with a colon as part of its print representation. The colon is not part of the symbol though.

On the other hand when you intern a symbol from a string without specifying the package it should be interned in it gets interned in the current package. So you were trying to intern the symbol |x| in the keyword package, but instead you were interning the symbol |:x| in whatever package you were in (and that leaves aside the fact that you almost certainly wanted |X| because the reader upcases things, but...). Boy, reading over that, I see it is as clear as mud... The point is that the colon is not part of the symbol there- when you use that in code meant to be read by the normal reader the colon is just shorthand for "this is a symbol in the keyword package". But.. you are also allowed to have a symbol with a name that actually begins with a colon in any package... claro ;)?

If not, don't worry... this is just a variation on who shaves the barber, and it becomes so clear after a while that you lose the ability to explain it adequately ;). Just keep programming, and asking questions.

I'd also note that people do seem to be able to write pretty sophisticated programs in scheme, and that scheme is a bit like a CL with none of these confusing features. I'm not suggesting that you abandon CL, I'm just pointing out that you don't have to know much about the reader or packages to get started in CL. And you can sidestep the whole keyword thing by never trying to intern symbols beginning with a colon. You shouldn't need to, and if you find yourself resorting to code like the code you posted you can be pretty sure that there is a better way to do things. CL is sometimes a bit inelegant, but...

Re: converting macro parameter name to key parameter

Posted: Mon Jun 01, 2009 1:55 pm
by Paul Donnelly
There's a simple solution in this case.

Code: Select all

CL-USER> (defun versor (axis) (make-3d-point axis 1))
CL-USER> (versor :x)
#S(3D-POINT :X 1 :Y 0 :Z 0)

Re: converting macro parameter name to key parameter

Posted: Tue Jun 02, 2009 9:10 am
by findinglisp
duncan wrote:Anyway, if some of that is Greek to you, don't feel bad. There are some aspects of CL that are a bit difficult to follow at first. I think the problem here is that you're getting a bit ahead of yourself (and I don't mean to patronize you here- CL is a really big complicated language that takes some time to understand, and on top of that it is very different from most of the languages in common use these days.)

The thing is that there's almost always a simple way to do things in CL, as well as a complicated way. So I'd suggest sticking with the stuff you understand really thoroughly while also reading about things like the keyword package and the reader. You'll find, I think, that CL is as capable a language as most others even without the bells and whistles.
I'll second this. If you're a newbie, then you just wandered into the advanced class and it's no wonder that you're lost. :shock: You'll understand this stuff eventually, but you need to get the underpinnings first. As Duncan says, I think you'll find that CL is a very capable language, even in the beginners class.

I would suggest you either buy a copy of Graham's ANSI Common Lisp or Seibel's Practical Common Lisp (also readable online) and read through them. They'll start you off with the basics and move you into the advanced class. By the time you are done with them, you'll be ready for advanced symbol manipulation and reader esoterica. :)