Search code examples
emacscommon-lispslimeccleval-when

Common Lisp: How to get (in-package ...) to work from within Emacs Slime


64-bit Windows 7
Clozure Common Lisp Version 1.9 WindowsX8632
Emacs 24.3.1
Slime changelog date 2014-06-17

I have an example .lisp file which starts out as follows:

(ql:quickload 'qt)
(in-package "QT")

The rest of the program shows a dialog box. When I run this from the command line, wx86cl -load helloqt.lisp it seems to work fine. When I run this from Emacs Slime (C-x C-k) it says there is no package "QT". However if I first evaluate the first line individually (C-x C-e) then I can compile the whole thing and it works, modulo the normal issues of trying to run a QT thread from within Slime on Windows.

How do I make it so I can compile/run the file from emacs without having to manually evaluate the first line first?

Also, why doesn't (in-package ...) change the current package in the Slime session? I have to change it manually if I want to interact with the package contents.


Solution

  • When you compile the file as a whole, it is first read as a whole. At that time, none of it has yet been evaluated, so the package QT is not defined yet.

    You can either use eval-when to evaluate something at an earlier time, or use a system definition facility (ASDF is predominant nowadays) to load your system in the right order.

    Eval-when:

    (eval-when (:compile-toplevel :load-toplevel :execute)
      (ql:quickload '#:qt))
    
    (in-package #:qt)
    

    Note that you usually should not muck around in library packages but define your own, fresh package to hold your code:

    (eval-when (:compile-toplevel :load-toplevel :execute)
      (ql:quickload '#:qt))
    
    (defpackage #:qt-example
      (:use #:qt))
    
    (in-package #:qt-example)
    
    ;; your code here
    

    (In case you are wondering, defpackage, defun, defclass etc. are specially designed macros that expand to a form inside such an eval-when.)

    This is sometimes OK for little one-off scripts. For systems of any noteworthy size, especially as soon as they have more than one source file, use ASDF:

    ;;;; qt-example.asd
    
    (in-package #:cl-user)
    
    (asdf:defsystem #:qt-experiments
      :description "Some experiments with QT."
      :serial t
      :components ((:file "package")
                   (:file "qt-example"))
      :depends-on (#:qt))
    
    ;;;; package.lisp
    
    (defpackage #:qt-example
      (:use #:qt))
    
    ;;;; qt-example.lisp
    
    (in-package #:qt-example)
    

    ASDF comes with most open-source Common Lisp implementations. You might need to set up the ASDF registry. I like to have one or two base directories for all my local projects, so that I can just put the following into ~/.config/common-lisp/source-registry.conf:

    (:source-registry
      (:tree (:home "devel"))
      (:tree (:home "src"))
      :inherit-configuration)
    

    Then ASDF finds all systems defined below those directories. In SLIME, you can just use ,load-system or ,open-system from the REPL with the system name to load it, resp. open all files in it, optionally loading it.

    When compiling a single toplevel form (using C-c C-c) from a file, SLIME looks backward from there for an in-package form to find out what package it should assume. Conventionally, you should only have a single in-package form per file, at its top.

    A commonly useful shortcut is C-c ~ in Lisp source files, which switches the REPL to the directory of the file and the effective package at point.