leetwinski
leetwinski

Reputation: 17849

Properly load-file for interactive Common Lisp development

I would like to know, what is the common approach to common-lisp interactive development in emacs (i use sly, but i think the slime instructions should be the same)

say i have this file:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload :closer-mop))

(in-package :cl-user)

(defpackage :shapes
  (:use :closer-common-lisp-user)
  (:export #:rectangle))

(in-package :shapes)

(defclass rectangle ()
  ((height :initform 0.0 :initarg :height)
   (width :initform 0.0 :initarg :width)))

which is quite simple. Evaluating it experssion by expression seems to be ok, while loading the whole file (C-c C-l) gives me the following error:

The variable SHAPES:RECTANGLE is unbound.
   [Condition of type UNBOUND-VARIABLE]

stripping it down to

(in-package :cl-user)

(defpackage #:shapes
  (:use #:cl-user)
  (:export #:rectangle))

(in-package #:shapes)

(defclass rectangle ()
  ((height :initform 0.0 :initarg :height)
   (width :initform 0.0 :initarg :width)))

doesn't make any change.

compile-and-load (C-c C-k) doesn't work either, leaving me with:

; in: DEFCLASS RECTANGLE
;     (SHAPES::DEFCLASS SHAPES:RECTANGLE NIL
;      ((SHAPES::HEIGHT :INITFORM 0.0 :INITARG :HEIGHT)
;       (SHAPES::WIDTH :INITFORM 0.0 :INITARG :WIDTH)))
; 
; caught COMMON-LISP:STYLE-WARNING:
;   undefined function: SHAPES::DEFCLASS

i see that defclass can't be properly resolved to from cl-user:defclass, but can't see the way to fix it.

I wonder what am i missing? And what is the common flow for developing interactively in emacs?

Upvotes: 3

Views: 396

Answers (1)

user5920214
user5920214

Reputation:

The underlying problem here is that you are confusing two ways that packages can be used in CL. A package generally serves one, or both, of two purposes:

  1. it can export a number of symbols, providing some kind of interface to functionality;
  2. it can be a package in which other packages are used or from which symbols are imported, but which does not export any symbols (unless it is also a type 1 package).

There is no formal distinction between these types of packages, but there very often is an informal distinction. Packages which are of the second type above are often called *-USER with the canonical example being the CL-USER package. They often (but not always) serve as places for scratch work.

So what you are doing is defining a package whose use list is such a user package. You can see that this is not going to work by simply looking at the external symbols of this package. From your second example:

> (do-external-symbols (s (find-package "CL-USER"))
    (print s))
nil

In other words, CL-USER exports no symbols at all. This means that your SHAPES package will initially not have access to any symbols at all, and in particular none of the CL symbols will be present.

Well, the language defines a canonical 'type 1' package, which is CL: the whole purpose of this package is to export the symbols which define the Common Lisp language, and only those symbols. So the definition of the SHAPES packages in your second example should be

(defpackage #:shapes
  (:use #:cl)
  (:export #:rectangle))

(Note that SHAPES is a type 1 package: it is providing some functionality in the form of SHAPES:RECTANGLE, and presumably is therefore intended to be used by other packages.)

Closer to MOP provides two packages which mirror the standard CL and CL-USER packages:

  • CLOSER-COMMON-LISP is like CL except that various symbols are replaced by ones defined by Closer to MOP, and there may be additional MOP symbols;
  • CLOSER-COMMON-LISP-USER is like CL-USER: it's a package intended general use, which users CLOSER-COMMON-LISP but which does not export any symbol at all.

Upvotes: 6

Related Questions