JohnJ
JohnJ

Reputation: 4833

Auto-load dependent files in REPL

I'm fairly new to Common Lisp, coming from Clojure, and am used to something like this:

(ns x.core
  (:require [x.util :as u]))

when I start a REPL and evaluate that file, x.util is automatically compiled and available to me in x.core.

In Common Lisp I'm trying to do something similar. In main.lisp:

(defpackage x.main
  (:use :x.util))

(in-package :x.main)

(comment
 (load "util.lisp"))

and in util.lisp:

(defpackage x.util
  (:use :common-lisp)
  (:export :foo))

(in-package :cl-blog.util)

(defun foo () 3)

the only way I know to have access to foo from util in main is to evaluate the form inside the comment macro (which I define similar to Clojure's comment, to ignore its body).

I have also tried this x.asd file:

(defsystem "x"
  :version "0.1.0"
  :author ""
  :license ""
  :components ((:module "src"
                        :components
                        ((:file "util")
                         (:file "main" :depends-on ("util")))))
  :description ""
  :in-order-to ((test-op (test-op "x/tests"))))

but that doesn't seem to help me with this problem.

Is there a simpler and more standard way to automatically load (or re-compile) util.lisp when I compile main.lisp in the REPL? What's the standard workflow for working with multiple files in the REPL?

Upvotes: 2

Views: 448

Answers (2)

Svante
Svante

Reputation: 51501

The thing you show for Clojure only works when the required namespace is in a file on the classpath, in a file and directory with names that match the namespace by convention. In order to manage the classpath (among other things), you use something like deps, boot, or leiningen.

In Common Lisp parlance, this is called system definition, and it works a bit differently.

The de facto standard tool for this is ASDF (Another System Definition Facility). While in Clojure you first determine the classpath and then start the entire application at once with it, in Common Lisp, you first start the image and then load systems into it (a bit similar to pomegranate in Clojure). Systems are defined in .asd files. ASDF knows several standard locations where to look for such files, and you can add more both through a configuration file or even at runtime.

It is important to realize that systems and packages are completely orthogonal concepts for Common Lisp. One system may define several packages, but things in one package might also be defined in different systems. Also, packages have no relation to files.

So, in short, your x.asd is absolutely OK. If you have in your system definition

:components ((:file "util")
             (:file "main")))

Then your util.lisp might be:

(defpackage #:x.util
  (:use #:cl))

(in-package #:x.util)

(defun foo ()
  'whatever)

And your main.lisp:

(defpackage #:x.main
  (:use #:cl))

(in-package #:x.main)

(defun bar ()
  (x.util:foo))

In order to load this system, you call (asdf:load-system "x") in the REPL.

You don't need to do anything then to enable referencing the other package. The other package is already there, because ASDF loaded all the files declared as components in the system definition.

To recapitulate: instead of starting an image with a complete classpath definition and then loading a particular file to make Clojure load the dependencies first, recursively, you start a basic image and then completely load one or more defined systems in a correct order of dependency.

It is common in Common Lisp to not define one package per file. Instead, a package is defined in a separate file, and other files then only have the in-package form at their top.

foo.asd:

(defsystem "foo"
  :serial t
  :components ((:file "package")
               (:file "utils)
               (:file "foo")))

package.lisp:

(in-package #:cl-user)

(defpackage #:foo
  (:use #:cl))

utils.lisp:

(in-package #:foo)

(defun frobnicate (bar)
  #| … |#)

foo.lisp:

(in-package #:foo)

(defun handle-vie (r)
  (wurble (frobnicate r)))

Many small libraries only have a single package, while bigger systems often have more and then also use some kind of pseudo-hierarchy (foo.bar, foo.baz).

There is also a newer addition to ASDF called package inferred system, which is in some ways more similar to the Clojure/Java mechanisms, but personally, I'm not convinced that this is a generally useful thing.

Upvotes: 4

Rainer Joswig
Rainer Joswig

Reputation: 139261

Manually this would be:

File main.lisp

(eval-when (:load-toplevel :compile-toplevel :execute)
  (load (merge-pathnames "utils.lisp" *load-pathname*)))

(defpackage x.main
  (:use :x.util))

(in-package :x.main)

(foo)

file util.lisp

(defpackage x.util
  (:use :common-lisp)
  (:export :foo))

(in-package :x.util)

(defun foo () 3)

Then call (load "/my/path/main.lisp").

More complex stuff would compile the file util.lisp if needed (if there is no compiled file or the lisp file would be newer)... and would then load the compiled code.

Otherwise you would define a system with two files.

main.lisp

(defpackage x.main
  (:use :x.util))

(in-package :x.main)

file util.lisp

(defpackage x.util
  (:use :common-lisp)
  (:export :foo))

(in-package :x.util)

(defun foo () 3)

Then define an ASDF system "my-system" (util.lisp is needed first and then main.lisp), load that system definition and call (asdf:load-system "my-system")... This would then load all files in the specified order/dependency.

Upvotes: 2

Related Questions