xhash101

joined 1 year ago
[–] xhash101@alien.top 1 points 1 year ago

Thanks a lot to everybody!

[–] xhash101@alien.top 1 points 1 year ago (1 children)

Thanks a lot!

If you've referenced these symbols by name in the body of the form, you already knew you needed them,

Yes, but I do not know how many of them. The purpose of this macro is to create a lexical environment to test a network code. Some functions take just one port as a parameter, but the others - two and more. With this macro it would be very easy to call these functions and to link their IO through the port numbers:

(with-free-ports 0 10
  (fn-1 port-1 port-2 port-3)
  (fn-2 port-1))

And with the first posted version of with-free-ports I achieved it. The problems appeared when I tried to bind start in run-time. From your reply and from the u/lispm comments I understood that this is not a good idea. What I do not understand, is how to use such macros inside another macros/functions, which can supply with-free-ports with start and end parameters ? Also, I cannot figure out, when exactly macro-expansions take place if I use many enclosed macros. Are they expanded all at once when I compile my code?

Anyway, I am very grateful for your suggestions. This helps a lot to learn CL.

[–] xhash101@alien.top 1 points 1 year ago (1 children)

Thank you for your reply.

The compiler generally will not execute the LET and then compile the WITH-FREE-PORTS form with the new binding

Here is an example when the compiler does exactly that:

(defmacro with-free-ports (start end &body body)
  `(list ,start ,end ,@body))

(let ((start 1)
      (end 3))      
  (with-free-ports start end NIL))

So, why does it work?

 

Hello guys,

I have a question regarding the nested backquotes in macros. I wrote a macro, which creates lexical bindings for "port:ip" values:

(defun mkstr (&rest args)
  (with-output-to-string (s)
    (dolist (a args) (princ a s))))

(defun mksymb (&rest args)
  (values (intern (string-upcase (apply #'mkstr args)))))

;; my macro
(defmacro with-free-ports (start end &body body)
  (let ((range (loop for port from start to end collect (format NIL "127.0.0.1:~a" port)))
	(n 0))
    `(let ,(mapcar #'(lambda (p) `(,(mksymb "PORT-" (incf n)) ,p)) range)
       (progn ,@body))))

One sets a range of ports on localhost and these ports are bound to symbols port-1, port-2, etc..

(with-free-ports 1 3 port-1) ;; => "127.0.0.1:1"

This works fine if the start or end parameters are given as values. But if they are variables. which must be evaluated, this macro doesn't work:

(let ((start 1))
  (with-free-ports start 3 port-1)) ;; error

In order to fix it, I made the let- bindings a part of the macro-expansion:

(defmacro with-free-ports (start end &body body)
  `(let ((range (loop for port from ,start to ,end collect (format NIL "127.0.0.1:~a" port)))
	(n 0))
     `(let ,(mapcar #'(lambda (p) `(,(mksymb "PORT-" (incf n)) ,p)) range)
	       (progn ,@body))))

but get a compilation warning that the body is never used. I assume this is because of the inner backquote.

To evaluate ,@body inside the inner backquote, I use one more comma, and the macro compiles without warnings:

(defmacro with-free-ports (start end &body body)
  `(let ((range (loop for port from ,start to ,end collect (format NIL "127.0.0.1:~a" port)))
	(n 0))
     `(let ,(mapcar #'(lambda (p) `(,(mksymb "PORT-" (incf n)) ,p)) range)
	       (progn ,,@body)))) ;; one more comma here

But it doesn't work:

(let ((start 1))
  (with-free-ports start 3 port-1)) ;; error: port-1 is unbound

because with this ,,@body I evaluate port-1: (progn ,port-1) and this triggers the error.

I would appreciate if smbd can help me a bit and say what I am doing wrong.

Thank you.

 
  1. A program, written in CL, is a huge mutable state. It seems that one can redefine nearly every symbol and there is no immutable data structures. But, as far as I understand, one can modify the language by using macros. So, is it possible to create a macro, which protects data structures from mutation or forbids the usage of mutable operators. For example:
(defmacro with-immutable-scope (&body body)
  ...)
(with-immutable-scope
  (let ((q (list 1)))
    (setf q 1))) => compilation error
  1. A public interface of a CL package consists of symbols. How can I specify and find out, what a symbol from a different package refers to? Should I do the following:

To specify what I export:

(defpackage :foo
  (:use :cl)
  (:export
   ;; macros
   :with-immutable-scope
   ;; functions
   :fetch-data
   ...

To find out what I import:

(describe fetch-data)
  1. When I create a variable binding with `let` and then modify the variable, this modification doesn't propagate through the binding. Example:
(defstruct point x)
 (let* ((point-obj (make-point :x 1))                                                      
        (x-ref (point-x point-obj)))                                                       
  (setf x-ref 2)                                                                          
  (point-x point-obj)) ;; => returns 1 because setf changed the reference to point-x but not the point-x itself

Does it mean that the let-bindings are effectively read-only pointers?

  1. How can I remove a method, which was previously associated with a generic function? For example:
(defgeneric foo (x))
(defmethod foo ((x list))
	   "list")
(defmethod foo ((x integer))
	   "integer")
(fmakeunbound-for-clos-methods '(foo (x integer))) ;; <- need help here
(foo '()) ;; => "list"
(foo 1)   ;; => NO-APPLICABLE-METHOD-ERROR

Does `fmakeunbound-for-clos-methods` exist ?