Subject: Re: Idiom to call function from string
From: rpw3@rpw3.org (Rob Warnock)
Date: Sun, 09 Jul 2006 20:13:55 -0500
Newsgroups: comp.lang.lisp
Message-ID: <LcSdnYZVPo5ONCzZnZ2dnUVZ_qOdnZ2d@speakeasy.net>
Sascha Wilde  <wilde@sha-bang.de> wrote:
+---------------
| I want to call an existing function after construction it's name from
| strings.  I came up with:
| (funcall (find-symbol (string-upcase (concatenate 'string 
|                                                   "foo-" "bar")) 
|                       :baz))
| to call function FOO-BAR from package BAZ, which actually works,
| but I'm under the impression that that's not very elegant -- so my
| question is: is there a standard idom to do this?
+---------------

From your description, I'm assuming that the "bar" is coming
from external user input, and the "foo-" is a prefix you use
in your application to distinguish functions which may be called
from such external input. If that's the case, you might want to
collect all the names of all the "foo-" functions into a hash
table[1], whereupon the above call turns into:

    (funcall (gethash "bar" *foo-functions*))

But both versions will give an "Undefined function: NIL" (or similar)
error if the input is invalid. The GETHASH version can be easily
adjusted to produce a more user-friendly result, e.g.:

    (funcall (gethash "bar" *foo-functions* 'foo-not-found-function)))

Now as far as *building* that hash table automagically, here's
a case where Common Lisp really shines!

    (defvar *foo-functions* (make-hash-table :test #'equal))

    (defmacro def-foo-function (name &body body)
      `(progn (defun ,name () ,@body)
	      (setf (gethash ,(string-downcase (symbol-name name))
			     *foo-functions*)
                    #',name)))

    (defun foo-dispatch (string)
      (flet ((usage ()
	       (format t "Unknown function: ~s~%~
			 usage: ...whatever...blah...blah...~%"
			 string)))
	(funcall (gethash string *foo-functions* #'usage))))

    ;;; Then simply define all your FOO- functions, e.g.:

    (def-foo-function hello
      (format t "Hello, world!~%"))

    ;;; ...and the rest...

And then your application can simply evoke FOO-DISPATCH:

    > (foo-dispatch "ehllo")
    Unknown function: "ehllo"
    usage: ...whatever...blah...blah...
    NIL
    > (foo-dispatch "hello")
    Hello, world!
    NIL
    > 


-Rob

[1] The hash table needs to be at least an #'EQUAL one, if you're
    using strings as inputs. If you want to ignore input case, then
    make the hash table definition above use :TEST #'EQUALP, and
    remove the STRING-DOWNCASE (or not) from the DEF-FOO-FUNCTION
    definition.

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607