Search code examples
lispcommon-lisp

Problem with package communication Common-Lisp


I have two packages defined: game (file game.lisp) and commands (file commands.lisp), loaded by a game.asd file. I'm having trouble in calling commands functions (that have been exported), using the (symbol-function (find-symbol "test-function" 'commands)), which returns that the function is undefined, even if the (find-symbol "test-function" 'commands) returned that the function is external, and belongs to the commands package.

The code on the game.asd file is:

(asdf:defsystem "game"
  :depends-on (#:cl-ppcre)
  :components ((:file "game")
               (:file "commands")))

The game.lisp starts with:

(defpackage :game
  (:use :cl :cl-ppcre))

The commands.lisp starts with:

(defpackage :commands
  (:use :cl)
  (:export "test-function"))

Do I need to use the in-package function? From the game.lisp I call the commands stored in the commands.lisp file, and some of these calls some functions on the game.lisp, for example:

(defun test-function ()
  (progn
    (format *query-io* "Worked!~%")
    (start)))

The test-function is located on the commands package, but calls the start function, which belongs to the game.lisp.

I expect the calling of the test-function function, when I call (symbol-function (find-symbol "test-function" 'commands)).


Solution

  • Summary

    My main recommendation is that you should have separate packages that contain user commands and that contain your Lisp code.

    You do not have to create a separate package for each Lisp file you have.

    Details

    You do need the in-package macro (it is not a function!) to ensure that your code resides in the correct package, because defpackage merely creates the package, it does not change *package*.

    Thus I suggest the following:

    Files

    game.asd

    (asdf:defsystem "game"
      :depends-on (#:cl-ppcre)
      :components ((:file "package")
                   (:file "game" :depends-on ("package"))
                   (:file "commands" :depends-on ("package"))))
    

    package.lisp

    (defpackage :game
      (:use :cl :cl-ppcre))
    

    game.lisp

    (in-package #:game)
    
    ...
    

    commands.lisp

    (in-package #:game)
    
    ...
    
    (defconstant *commands-package* (make-package '#:commands :use nil))
    

    and then use intern to add commands to *commands-package* and find-symbol to find them.

    (defun test-command ()
      (format t "test-command~%")
      (start))
    (intern 'test-command *commands-package*)
    

    You could also define your own macro for that:

    (defmacro defcommand (name arglist &body body)
      `(progn
         (intern (symbol-name ',name) *commands-package*)
         (defun ,name ,arglist ,@body)))
    
    (defcommand test-command ()
      (format t "test-command~%")
      (start))
    

    Nitpicks