mercredi 24 novembre 2021

On using structure literals in function and macro definitions

IMPORTANT: This question is motivated solely by my wanting to learn Common Lisp. Beyond this autodidactic goal, it has no ulterior practical purpose.


If, at my REPL1, I evaluate the following expression

CL-USER> (random-state-p
  #S(random-state
       :state #.(make-array 627
                            :element-type '(unsigned-byte 32)
                            :initial-element 7)))

...I get T. In other words, the literal

#S(random-state
     :state #.(make-array 627
                          :element-type '(unsigned-byte 32)
                          :initial-element 7))

...evaluates to a valid random-state object.


So far so good.

Now, I try to define a function, silly-random-state, so that the expression (silly-random-state 7) will produce the same random-state object as that produced by the literal above, like this:

(defun silly-random-state (some-int)
  #S(random-state
       :state #.(make-array 627
                            :element-type '(unsigned-byte 32)
                            :initial-element some-int)))

...but my REPL will have none of it! It won't even evaluate this defun! It complains that some-int is undefined2.

On further thought, I guess it makes sense that one cannot stick a variable inside a literal and expect the resulting expression to make sense... Maybe I need a macro for this?

So then I try to define a macro that, given an integer argument, will expand to such a random-state object, like this:

(defmacro silly-random-state-macro (some-int)
  `#S(random-state
        :state #.(make-array 627
                             :element-type '(unsigned-byte 32)
                             :initial-element ,some-int)))

Again, my REPL will have none of it. The evaluation of the expression above fails with

Comma inside backquoted structure (not a list or general vector.)


Each of the two failures above leads to a corresponding question:

  1. how can I define a function silly-random-state that takes an integer SOME-INT as argument, and returns the random-state equal to the one the REPL would produce if I gave it the expression below after replacing PLACEHOLDER with the integer SOME-INT?
#S(random-state
     :state #.(make-array 627
                          :element-type '(unsigned-byte 32)
                          :initial-element PLACEHOLDER))
  1. how can I define a macro silly-random-state-macro such that (silly-random-state-macro some-int) expands to a the same random-state object described in (1)?

(To repeat, my motivation here is only to better understand what can and cannot be done with Common Lisp3.)


1 SBCL + SLIME/Emacs running on Darwin.

2 BTW, my REPL had never been so picky before! For example, it will evaluate something like (defun foo () undefined-nonsense). Granted, it does grouse a bit over the undefined variable in the body, but in the end it will evaluate the defun.

3 In fact, I debated whether I should include [prng] among the tags for this question, since its topics are really literals, functions, and macros, and the role of CL's default PRNG in it is only incidental. I finally opted to keep this tag just in case the role of the PRNG in the question is less incidental than it seems to me.


EDIT: OK, I think I found a way to define the function I described in (1); it's not very elegant, but it works:

(defun silly-random-state (some-int)
  (read-from-string
   (format nil "#S(random-state :state #.(make-array 627 :element-type '(unsigned-byte 32) :initial-element ~A))" some-int)))



Aucun commentaire:

Enregistrer un commentaire