[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

A macro challenge (for me): Defining a set of vars

Frank GOENNINGER

12/27/2015 11:17:00 PM


Objective:

(define-config-var frgo 42)

shall expand into the followimg code:

(eval-when (:compile-toplevel :load-toplevel)
(defc +frgo+ 42)
(defv *frgo* #.+frgo+)
(export +frgo+)
(export *frgo*))

I have:

(defun mkstr (&rest args)
"Convert all args into a concatenated string."
(with-output-to-string (s)
(dolist (a args) (princ a s))))

(defmacro define-config-var (name val)
(alexandria:with-gensyms (g-default-var-name
g-var-name
g-default-var
g-var
g-var-name-str)
`(let* ((,g-var-name-str (mkstr ',name))
(,g-default-var-name (concatenate 'string "+" ,g-var-name-str "+"))
(,g-var-name (concatenate 'string "*" ,g-var-name-str "*")))
(defconstant (symbol-value ,g-default-var-name) ,val))

;; ... some code deleted as even this statement doesnt work ...
))

I. Am. Lost. - Would want to get this sorted out myself. Any small
hints appreciated.

Thx!

Best,
Frank

5 Answers

Pascal Costanza

12/27/2015 11:49:00 PM

0

On 28/12/2015 00:17, Frank DG1SBG wrote:
>
> Objective:
>
> (define-config-var frgo 42)
>
> shall expand into the followimg code:
>
> (eval-when (:compile-toplevel :load-toplevel)
> (defc +frgo+ 42)
> (defv *frgo* #.+frgo+)
> (export +frgo+)
> (export *frgo*))
>
> I have:
>
> (defun mkstr (&rest args)
> "Convert all args into a concatenated string."
> (with-output-to-string (s)
> (dolist (a args) (princ a s))))
>
> (defmacro define-config-var (name val)
> (alexandria:with-gensyms (g-default-var-name
> g-var-name
> g-default-var
> g-var
> g-var-name-str)
> `(let* ((,g-var-name-str (mkstr ',name))
> (,g-default-var-name (concatenate 'string "+" ,g-var-name-str "+"))
> (,g-var-name (concatenate 'string "*" ,g-var-name-str "*")))
> (defconstant (symbol-value ,g-default-var-name) ,val))
>
> ;; ... some code deleted as even this statement doesnt work ...
> ))
>
> I. Am. Lost. - Would want to get this sorted out myself. Any small
> hints appreciated.
>
> Thx!
>
> Best,
> Frank
>

The first argument to defconstant should be a symbol, which won't be
evaluated. (Creating fresh bindings for names is the purpose of
definitions, so evaluating the name position is kind of besides the
point, isn't it?)

What do you expect symbol-value to return anyway? Have you checked the
documentation? Have you tried it in the REPL?

Another hint: Strings are not symbols. There are functions for turning
strings into symbols. (When do you have to call them?)

Yet another hint: You will not be able to make a macro expand
meaningfully into a #. form. #. is for evaluating forms at read time,
which is looong before macroexpansion time. You can only make a macro
expand into something that will be evaluated later.

Pascal

--
My website: http:/...
Common Lisp Document Repository: http://cdr.eu...
Closer to MOP & ContextL: http://common-lisp.net/proje...
The views expressed are my own, and not those of my employer.

Pascal J. Bourguignon

12/28/2015 12:03:00 AM

0

Frank DG1SBG <dg1sbg@googlemail.com> writes:

> Objective:
>
> (define-config-var frgo 42)
>
> shall expand into the followimg code:
>
> (eval-when (:compile-toplevel :load-toplevel)
> (defc +frgo+ 42)
> (defv *frgo* #.+frgo+)
> (export +frgo+)
> (export *frgo*))

You cannot evaluate the value of +frgo+ when you are reading the form
that WILL in the future, perhaps in 10000 years, and perhaps in a galaxy
far far away, eventually define +frgo+.


> I have:
>
> (defun mkstr (&rest args)
> "Convert all args into a concatenated string."
> (with-output-to-string (s)
> (dolist (a args) (princ a s))))

This is bad, because the output of PRINC depends on variables such as
*PRINT-CASE*.


cl-user> (princ 'hello)
hello
hello
cl-user> (princ (symbol-name 'hello))
HELLO
"HELLO"
cl-user> *print-case*
:downcase
cl-user>

At the very least, you should use WITH-STANDARD-IO-SYNTAX here, but I
would advise to just use symbol-name for symbols.


> (defmacro define-config-var (name val)
> (alexandria:with-gensyms (g-default-var-name
> g-var-name
> g-default-var
> g-var
> g-var-name-str)
> `(let* ((,g-var-name-str (mkstr ',name))
> (,g-default-var-name (concatenate 'string "+" ,g-var-name-str "+"))
> (,g-var-name (concatenate 'string "*" ,g-var-name-str "*")))
> (defconstant (symbol-value ,g-default-var-name) ,val))
>
> ;; ... some code deleted as even this statement doesnt work ...
> ))

The name is known at compilation time, therefore the generated names can
be known at compilation time too, therefore you don't need with-gensym,a
nd you don't need to bind them at run-time.

Run time is TOO LATE to compute those variable names, since defconstant
etc, need to have the variable name AT COMPILATION TIME!


Also, buying a clock or a watch might help you get some notion of time.


> I. Am. Lost. - Would want to get this sorted out myself. Any small
> hints appreciated.

You need a time machine to zap all around the clock, or you need to sort
out your times.

(eval-when (:compile-toplevel :load-toplevel :execute)
(defun generate-define-config-var-code (name value)
(let ((cname (intern (concatenate
'string "+" (symbol-name name) "+")
(symbol-package name)))
(vname (intern (concatenate
'string "*" (symbol-name name) "*")
(symbol-package name))))
`(eval-when (:compile-toplevel :load-toplevel)
(defconstant ,cname 42)
(defvar ,vname ,cname)
(export '(,cname ,vname))))))

(generate-define-config-var-code 'frgo 42)
;; --> (eval-when (:compile-toplevel :load-toplevel)
;; (defconstant +frgo+ 42)
;; (defvar *frgo* +frgo+)
;; (export '(+frgo+ *frgo*)))


(defmacro define-config-var (name value)
(generate-define-config-var-code name value))

(macroexpand '(define-config-var frgo 42))
;; --> (eval-when (:compile-toplevel :load-toplevel)
;; (defconstant +frgo+ 42)
;; (defvar *frgo* +frgo+)
;; (export '(+frgo+ *frgo*)))
;; t



Also, I find it strange that you don't want to create the configuration
variable in the REPL (or loading the configuration source file):

cl-user> (define-config-var frgo 42)
nil
cl-user> +frgo+ ; since we don't define in the :execute situation:
> Debug: Unbound variable: +frgo+
> While executing: (:internal swank::invoke-default-debugger), in process repl-thread(1915).
> Type :GO to continue, :POP to abort, :R for a list of available restarts.
> If continued: Retry getting the value of +frgo+.
> Type :? for other options.
1 > :q
; Evaluation aborted on #<unbound-variable #x30235F343D5D>.

cl-user> (with-open-file (src "/tmp/conf.lisp"
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(print '(define-config-var frgo 42) src))
(define-config-var frgo 42)
cl-user> (load (compile-file "/tmp/conf.lisp"))
#P"/tmp/conf.lx64fsl"
cl-user> +frgo+
42
cl-user> *frgo*
42
cl-user>

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Frank GOENNINGER

12/28/2015 12:28:00 PM

0

Pascal Costanza <pc@p-cos.net> writes:

> The first argument to defconstant should be a symbol, which won't be
> evaluated. (Creating fresh bindings for names is the purpose of definitions,
> so evaluating the name position is kind of besides the point, isn't
> it?)

Yes, that was clear. Still the question was how to come from a symbol to
the name to be interned. I simply was thinking too complicated.
>
> What do you expect symbol-value to return anyway? Have you checked the
> documentation? Have you tried it in the REPL?

Yes, I had. But didn't see what I did wrong.
>
> Another hint: Strings are not symbols. There are functions for turning strings
> into symbols. (When do you have to call them?)

Yep, intern. That was clear also. Still ...

> Yet another hint: You will not be able to make a macro expand meaningfully
> into a #. form. #. is for evaluating forms at read time, which is looong
> before macroexpansion time. You can only make a macro expand into something
> that will be evaluated later.

Yes, thanks. I meanwhile (after a few hours of sleep) recognized as much.

>
> Pascal

Thanks, Pascal, for taking the time to pinpoint the wrong points.

- Frank

Frank GOENNINGER

12/28/2015 12:41:00 PM

0

"Pascal J. Bourguignon" <pjb@informatimago.com> writes:

> Frank DG1SBG <dg1sbg@googlemail.com> writes:
>
>> Objective:
>>
>> (define-config-var frgo 42)
>>
>> shall expand into the followimg code:
>>
>> (eval-when (:compile-toplevel :load-toplevel)
>> (defc +frgo+ 42)
>> (defv *frgo* #.+frgo+)
>> (export +frgo+)
>> (export *frgo*))
>
> You cannot evaluate the value of +frgo+ when you are reading the form
> that WILL in the future, perhaps in 10000 years, and perhaps in a galaxy
> far far away, eventually define +frgo+.
>
>

Yes, clear.

>> I have:
>>
>> (defun mkstr (&rest args)
>> "Convert all args into a concatenated string."
>> (with-output-to-string (s)
>> (dolist (a args) (princ a s))))
>
> This is bad, because the output of PRINC depends on variables such as
> *PRINT-CASE*.

Ouch - didn't think of that. Thanks.

> cl-user> (princ 'hello)
> hello
> hello
> cl-user> (princ (symbol-name 'hello))
> HELLO
> "HELLO"
> cl-user> *print-case*
> :downcase
> cl-user>
>
> At the very least, you should use WITH-STANDARD-IO-SYNTAX here, but I
> would advise to just use symbol-name for symbols.

Noted.

>> (defmacro define-config-var (name val)
>> (alexandria:with-gensyms (g-default-var-name
>> g-var-name
>> g-default-var
>> g-var
>> g-var-name-str)
>> `(let* ((,g-var-name-str (mkstr ',name))
>> (,g-default-var-name (concatenate 'string "+" ,g-var-name-str "+"))
>> (,g-var-name (concatenate 'string "*" ,g-var-name-str "*")))
>> (defconstant (symbol-value ,g-default-var-name) ,val))
>>
>> ;; ... some code deleted as even this statement doesnt work ...
>> ))
>
> The name is known at compilation time, therefore the generated names can
> be known at compilation time too, therefore you don't need with-gensym,a
> nd you don't need to bind them at run-time.
>
> Run time is TOO LATE to compute those variable names, since defconstant
> etc, need to have the variable name AT COMPILATION TIME!
>
>
> Also, buying a clock or a watch might help you get some notion of
> time.

Santa was not kind enough to bring me the Breitling Navitimer I so much
wished for. So, sorry, but time is still a relative concept for me.

(I had a good laugh when reading your comment on the watch - and, due to the
quite loud laugh, then had to explain all this to my son. Result: Nice
discussion about Lisp macros and the specialties around them. Special
thanks for this !)

>> I. Am. Lost. - Would want to get this sorted out myself. Any small
>> hints appreciated.
>
> You need a time machine to zap all around the clock, or you need to sort
> out your times.

You know, the Navitimer ...

>
> (eval-when (:compile-toplevel :load-toplevel :execute)
> (defun generate-define-config-var-code (name value)
> (let ((cname (intern (concatenate
> 'string "+" (symbol-name name) "+")
> (symbol-package name)))
> (vname (intern (concatenate
> 'string "*" (symbol-name name) "*")
> (symbol-package name))))
> `(eval-when (:compile-toplevel :load-toplevel)
> (defconstant ,cname 42)
> (defvar ,vname ,cname)
> (export '(,cname ,vname))))))
>
> (generate-define-config-var-code 'frgo 42)
> ;; --> (eval-when (:compile-toplevel :load-toplevel)
> ;; (defconstant +frgo+ 42)
> ;; (defvar *frgo* +frgo+)
> ;; (export '(+frgo+ *frgo*)))
>
>
> (defmacro define-config-var (name value)
> (generate-define-config-var-code name value))
>
> (macroexpand '(define-config-var frgo 42))
> ;; --> (eval-when (:compile-toplevel :load-toplevel)
> ;; (defconstant +frgo+ 42)
> ;; (defvar *frgo* +frgo+)
> ;; (export '(+frgo+ *frgo*)))
> ;; t

This clearly shows that I was indeed messing up with times and also
that trying to solve trivial problems after more than 8 hours may
occasionally turn into embarrasing questions that still spark
new enlightenment by always providing new insights from you folks of c.l.l.

> Also, I find it strange that you don't want to create the configuration
> variable in the REPL (or loading the configuration source file):
>
> cl-user> (define-config-var frgo 42)
> nil
> cl-user> +frgo+ ; since we don't define in the :execute situation:
>> Debug: Unbound variable: +frgo+
>> While executing: (:internal swank::invoke-default-debugger), in process
>> repl-thread(1915).
>> Type :GO to continue, :POP to abort, :R for a list of available restarts.
>> If continued: Retry getting the value of +frgo+.
>> Type :? for other options.
> 1 > :q
> ; Evaluation aborted on #<unbound-variable #x30235F343D5D>.
>
> cl-user> (with-open-file (src "/tmp/conf.lisp"
> :direction :output
> :if-does-not-exist :create
> :if-exists :supersede)
> (print '(define-config-var frgo 42) src))
> (define-config-var frgo 42)
> cl-user> (load (compile-file "/tmp/conf.lisp"))
> #P"/tmp/conf.lx64fsl"
> cl-user> +frgo+
> 42
> cl-user> *frgo*
> 42
> cl-user>

Thanks, yes, I know but there's more code around all this than I showed
here. It may still turn out that I need the execution time included in
the eval-when.

Regards
Frank

Pascal J. Bourguignon

12/28/2015 7:13:00 PM

0

Frank DG1SBG <dg1sbg@googlemail.com> writes:

> Thanks, yes, I know but there's more code around all this than I showed
> here. It may still turn out that I need the execution time included in
> the eval-when.

Let's say that it's useful for debugging and interactive testing.

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk