This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Macro to construct function names dynamically

5 posts · 2669 views

Hello,

I'm new to Lisp and my previous programming experience was mostly C, Perl and shell scripts. I'm still heavily influenced by it so please bear with me.
So: Is there a way to define a macro which constructs a function name when given a list of symbols? I'd like to call it like this:
(do-expand-functions '(first second third fourth fifth) '(a b c) (list foo bar baz quux))
and get as a result
(progn
  (do-function-a-first foo)
  (do-function-a-second foo)
  (do-function-a-third foo)
  (do-function-a-fourth foo)
  (do-function-a-fifth foo)
  (do-function-b-first bar)
  (do-function-b-second bar)
  .
  .
  .
  (do-function-c-fifth quux))
How do I construct the do-function-* names? Once I get past that the rest is easy. Is there a common idiom for this kind of things? If I were using Bash I'd probably say
for I in a b c; do
    for J in first second third; do
        for K in foo bar baz quux; do
            eval do_function_${I}_${J} $K
        done
    done
done
although it's not quite the same.

Thank you in advance.

Re: Macro to construct function names dynamically

Here are a few tools at your disposal:
(symbol-name 'a) ; "A"
(string 'A); "A"
(format nil "~{~A~}" '(a b c)) ; "ABC"
(intern "C") ; 'C
(make-symbol "D") ; '#:D
The first three lines show ways of getting strings from symbols; the second show the opposite transform. There are several things to be careful of here; you might want to call these inside with-standard-io-syntax, bind *print-case*, etc. There are a couple existing functions to do this but none have widespread popularity.

Part of the problem is that people can customize the CL reader and printer. Part of the problem is getting the symbols in the right package. In the end, some experienced users don't like machine-generated names. They prefer APIs which allow you to specify the name for everything (see for example defstruct vs defclass). Also, it is often better to use generic functions or a data structure (e.g. plist, hashtable, or array) than constructed names.

Re: Macro to construct function names dynamically

foregam wrote:
(do-expand-functions '(first second third fourth fifth) '(a b c) (list foo bar baz quux))
Since it looks like you want the arguments to be evaluated at run-time, you may want a function rather than a macro.

In that case, you can do it basically the same way as in Bash, except without using eval. In addition to the functions that nuntius has referenced, funcall is your friend.

Re: Macro to construct function names dynamically

Thank you both very much for your concise answers. @Kompottkin, no, I actually want it as a shorthand for writing a big number of similarly looking lines by hand, which I consider a very likely source of errors. Maybe I should refactor my source further on. The Bash example I gave was simplified to make my general intent clear.

Re: Macro to construct function names dynamically

Here is a version of a macro which creates function calls according to every possable combination of concatination of the symbols in symbol-list as the functions, and every possable concatination of the args. If an atom is applied anywhere, it is included in its place according to the pattern.
(defmacro funcall-matrix (symbol-list args-list)
	   (let ((functions (apply #'symbol-matrix symbol-list))
		 (args (apply #'map-each-to-every args-list)))
	     (cons 'list
		   (mapcar #'(lambda (elt)
			       (cons (car elt) (cadr elt)))
			   (map-each-to-every functions args)))))

(defun symbol-matrix (&rest symbols)
	   (mapcar 
	  #'(lambda (symbol-list)
	      (apply #'symbol-concat symbol-list))
	  (apply #'map-each-to-every symbols)))

(defun map-each-to-every (&rest atoms-or-lsts)
	   (labels ((mp (&rest lsts)
		      (if (cdr lsts)
			  (apply #'mp
				 (cons 
				  (apply #'append
					 (mapcar #'(lambda (each-elt)
						     (mapcar #'(lambda (every-elt)
								 (append each-elt (list every-elt)))
							     (ensure-list (second lsts))))
						 (ensure-list (first lsts))))
				  (cddr lsts)))
			  (first lsts))))
	     (apply #'mp 
		    (cons (mapcar #'list (ensure-list (car atoms-or-lsts)))
			  (cdr atoms-or-lsts)))))

(defun symbol-concat (&rest symbols)
	   (intern
	    (apply #'string-concat
		   (mapcar 
		    #'(lambda (elt)
			(if (symbolp elt)
			    (symbol-name elt)
			    (write-to-string elt)))
		    symbols))))

(defun ensure-list (elt)
	   (if (listp elt)
	       elt
	       (list elt)))

For example, calling
>(macroexpand '(funcall-matrix ((a b c) - (1 2 3 4)) ((arg-a arg-b) 15 (arg-x arg-y arg-z))))

(LIST (A-1 ARG-A 15 ARG-X) (A-1 ARG-A 15 ARG-Y) (A-1 ARG-A 15 ARG-Z)
(A-1 ARG-B 15 ARG-X) (A-1 ARG-B 15 ARG-Y) (A-1 ARG-B 15 ARG-Z) (A-2 ARG-A 15 ARG-X)
(A-2 ARG-A 15 ARG-Y) (A-2 ARG-A 15 ARG-Z) (A-2 ARG-B 15 ARG-X) (A-2 ARG-B 15 ARG-Y)
(A-2 ARG-B 15 ARG-Z) (A-3 ARG-A 15 ARG-X) (A-3 ARG-A 15 ARG-Y) (A-3 ARG-A 15 ARG-Z)
(A-3 ARG-B 15 ARG-X) (A-3 ARG-B 15 ARG-Y) (A-3 ARG-B 15 ARG-Z) (A-4 ARG-A 15 ARG-X)
(A-4 ARG-A 15 ARG-Y) (A-4 ARG-A 15 ARG-Z) (A-4 ARG-B 15 ARG-X) (A-4 ARG-B 15 ARG-Y)
(A-4 ARG-B 15 ARG-Z) (B-1 ARG-A 15 ARG-X) (B-1 ARG-A 15 ARG-Y) (B-1 ARG-A 15 ARG-Z)
(B-1 ARG-B 15 ARG-X) (B-1 ARG-B 15 ARG-Y) (B-1 ARG-B 15 ARG-Z) (B-2 ARG-A 15 ARG-X)
(B-2 ARG-A 15 ARG-Y) (B-2 ARG-A 15 ARG-Z) (B-2 ARG-B 15 ARG-X) (B-2 ARG-B 15 ARG-Y)
(B-2 ARG-B 15 ARG-Z) (B-3 ARG-A 15 ARG-X) (B-3 ARG-A 15 ARG-Y) (B-3 ARG-A 15 ARG-Z)
(B-3 ARG-B 15 ARG-X) (B-3 ARG-B 15 ARG-Y) (B-3 ARG-B 15 ARG-Z) (B-4 ARG-A 15 ARG-X)
(B-4 ARG-A 15 ARG-Y) (B-4 ARG-A 15 ARG-Z) (B-4 ARG-B 15 ARG-X) (B-4 ARG-B 15 ARG-Y)
(B-4 ARG-B 15 ARG-Z) (C-1 ARG-A 15 ARG-X) (C-1 ARG-A 15 ARG-Y) (C-1 ARG-A 15 ARG-Z)
(C-1 ARG-B 15 ARG-X) (C-1 ARG-B 15 ARG-Y) (C-1 ARG-B 15 ARG-Z) (C-2 ARG-A 15 ARG-X)
(C-2 ARG-A 15 ARG-Y) (C-2 ARG-A 15 ARG-Z) (C-2 ARG-B 15 ARG-X) (C-2 ARG-B 15 ARG-Y)
(C-2 ARG-B 15 ARG-Z) (C-3 ARG-A 15 ARG-X) (C-3 ARG-A 15 ARG-Y) (C-3 ARG-A 15 ARG-Z)
(C-3 ARG-B 15 ARG-X) (C-3 ARG-B 15 ARG-Y) (C-3 ARG-B 15 ARG-Z) (C-4 ARG-A 15 ARG-X)
(C-4 ARG-A 15 ARG-Y) (C-4 ARG-A 15 ARG-Z) (C-4 ARG-B 15 ARG-X) (C-4 ARG-B 15 ARG-Y)
(C-4 ARG-B 15 ARG-Z))

I remeber when I started programming in Lisp, I was comming from Java and implemented a simple OO system. Its funny how influneced you are by the languages you use.