When I reset Slime, recompile and reinstall packages - why? - common-lisp

I have a SBCL package called frosty.
I am using SLIME with emacs. When I close emacs for the day, and then open it the next day, it's as if I am "starting again".
All my quicklisp packages need to be reloaded. And my frosty package needs to be compiled manually.
Is there a way to have the REPL automatically understand the packages/QL packages from the start?
Thanks

You can define a system to group your files as a unit that can be compiled as a whole, and once it is compiled once, the resulting object files are stored in cache and loaded faster the next time (without compilation).
defsystem
At the root of your frosty directory, you can add an ASDF system definition. It contains the following at minimum:
(defsystem "frosty"
;; system dependencies:
;; those are system names, not package names; they often are
;; identical but in general a system can define zero, one or
;; more packages
:depends-on ("alexandria"
"cl-ppcre"
;; etc.
)
;; this declares a list of component, here there is a single entry,
;; namely (:file "frosty") which represents a Lisp source file.
;; If you organized your files differently, e.g. under a src/ directory,
;; you need to add a :pathname option.
;; See https://asdf.common-lisp.dev/asdf.html#The-defsystem-grammar
:components ((:file "frosty"))
)
What we want to do is be able to call:
(ql:quickload "frosty")
And have Quicklisp install all the dependencies and your own system.
In order for quicklisp to know about your .asd file, your project needs to be visible in the quicklisp/local-projects directory. Alternatively, you can choose to link your project under ~/common-lisp/ directory, or any other directory searched by the ASDF central registry (see documentation).
.sbclrc
In your ~/.sbclrc init file, you can add expressions to be evaluated, and in particular you can write (ql:quickload "frosty") here so that it is executed each time SBCL is started.

One of the following will work.
Don't quit the running lisp / emacs. Only an option if you're able to leave processes running overnight on the machine you're using, but that usually is the case now.
Save a core image and restart that: See here and the --core option.
Write a little Lisp source file which just reloads everything, perhaps by using a system definition.
The first two are superficially attractive but have the consequence that, over time, you end up running in a world which you only may be able to recreate and which gradually becomes infested by elves (anyone who has used d machines seriously will remember this problem). The last option means you know how to recreate the state of the world, but you have to pay the cost of doing so.
Once upon a time when computers were slower most people did one of the first two. Now they're quick the last seems preferable in many cases.

Related

asdf:load-system after closing emacs?

When I create a new project with quickprojects, and then load it with asdf:load-system, everything works fine.
But when I come back to emacs after it has been closed and a run (asdf:load-system "system-name"), I get an error:
Component "next" not found
[Condition of type ASDF/FIND-COMPONENT:MISSING-COMPONENT]
I understood the package system to be like a project management system where I can load a package from the repl when I choose.
Which mistake am I making?
In quickproject.lisp, there is the following line:
(pushnew *default-pathname-defaults* asdf:*central-registry*
:test 'equal)
Which is the reason why the system can be loaded: the path to your new project is pushed into ASDF's *central-registry*, which acts like the PATH environment variable in POSIX to indicate where to look for systems.
This change is not persisted and the next time you start Lisp, the variable is set to its default value and the path is not set. I think Quickproject should at least warn that this is happening because it is not very intuitive1
Usually you should add your projects in a path that is visible by ASDF, as explained in 4.1 Configuring ASDF to find your systems. Pushing to *central-registry* works but is old style: 4.2 Configuring ASDF to find your systems — old style.
When using Quicklisp you can also define your projects in ~/quicklisp/local-projects/ (either directly or by using symlinks), which is a place that Quicklisp makes also visible by ASDF.
You can temporarily bind *default-pathname-defaults* to one of these known locations when creating your projects, so that it can be found again when restarting your environment:
(let ((*default-pathname-defaults*
(merge-pathnames "quicklisp/local-projects/"
(user-homedir-pathname)))
(quickproject:make-project "test-project"))
1. To Quickproject's defense, this is explained in the second sentence of the documentation:
Quickproject provides a quick way to make a Common Lisp project. After creating a project, it extends the ASDF registry so the project may be immediately loaded.

Adding a directory globally to cffi:*foreign-library-directories*

I am using the Nix package manager under macOS to install much of my software, including dynamic libraries. And I would like to make them accessible to CFFI. That means adding a path to cffi:*foreign-library-directories*. Fine, but how can I do this
globally for my system (should work for packages loaded via Quicklisp, for example)
without loading CFFI every time I start sbcl?
Ignoring the second criterion, I can just add a few lines to ~/.sbclrc:
(ql:quickload "CFFI")
(pushnew (merge-pathnames ".nix-profile/lib/" (user-homedir-pathname))
cffi:*foreign-library-directories*
:test #'equal)
What I am looking for is a way to add the path after CFFI is loaded. A bit like eval-after-load in Emacs Lisp. Is that possible?
I think that you should try to use the mechanisms of the underlying system instead, i. e. on Linux ldconfig (resp. ld.so.conf), on MacOS DYLD_LIBRARY_PATH. The CFFI manual says that the *foreign-library-directories* are only used as a fallback if the system mechanism fails.

Having to Re-install Quicklisp Packages on Each Restart

I am writing a (SBCL) Common Lisp program that has the following line at the top of the file:
(defpackage :my-package
(:use :cl :cl-who :hunchentoot :parenscript))
I am running Emacs 25, SBCL and SLIME on MacOS.
Whenever I evaluate the line above, I always get this error at first:
The name "CL-WHO" does not designate any package.
[Condition of type SB-KERNEL:SIMPLE-PACKAGE-ERROR]
Then, I run (ql:quickload "cl-who") and watch the CL-WHO package install. I repeat for the other two packages. Once I have done that, the form can be evaluated successfully.
The problem is that I need to do this each time I restart the Emacs (or Lisp process, which I assume is roughly equivalent in this case). Why is it that when I install something with Quicklisp it is not "remembered" for the next session? Am I doing something wrong?
Is this a problem with configurations or a general misunderstanding of how it is supposed to work?
Then, I run (ql:quickload "cl-who") and watch the CL-WHO package install. I repeat for the other two packages. Once I have done that, the form can be evaluated successfully.
You can quickload more than one system at once:
(ql:quickload '(:cl-who :hunchentoot :parenscript))
[...] each time I restart the Emacs (or Lisp process, which I assume is roughly equivalent in this case).
That's the case here, but notice that you can start a Lisp process from the shell and connect to it from Emacs. In that case, you could exit and restart Emacs without killing the Lisp process.
Start a new REPL from the shell and create a server:
(ql:quickload :swank)
(swank:create-server :port 5000)
And then, call slime-connect from Emacs (with localhost and 5000 for the host and port parameters). That can be used also to monitor a running application.
Why is it that when I install something with Quicklisp it is not "remembered" for the next session?
The system is fetched, compiled and installed on your machine, which explains why the second time you quickload a project, it is faster. But a system is only loaded in your environment when you request it, either with Quicklisp or ASDF.
Define a system
See §6. Defining systems with defsystem for an introduction on how to define a system. Suppose you name your system stackoverflow. The simplest way to get started is to create the following file:
~/quicklisp/local-projects/stackoverflow/stackoverflow.asd
which contains:
(defsystem "stackoverflow"
:depends-on ("cl-who" "hunchentoot" "parenscript"))
When you execute (ql:quickload "stackoverflow"), Quicklisp will load all its dependencies.
Instead of loading all the required systems, you only need to load a single one.
Either automatically load this system...
Lisp implementations can execute code at startup. One possible way for this is to execute the code from a configuration file: [.]ccl-init.lisp, .eclrc, .lispworks, .sbclrc (your case), etc. Execute quickload only if Quicklisp itself is available:
#+quicklisp
(ql:quickload "stackoverflow")
... or dump an image with all systems preloaded
You can also load all the required systems, and dump an executable image.
Start a fresh SBCL from a terminal (not from Slime), call (ql:quickload "stackoverflow"), and then:
(sb-ext:save-lisp-and-die "my-env"
:executable t
:compression 9)
(compression is optional)
Then, an executable file named "my-env" should be created in the same directory. Each time you start it, you have a fresh Lisp environment containing the systems you loaded before saving the image.

loading quicklisp in a program

What's the correct way to load quicklisp at the start of my program? I'm currently copy/pasting in the block that quicklisp inserted into my .eclrc, e.g.
;;; quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload "iterate")
but it seems like there has to be a less messy way to do things.
There isn't really a good way to do that. It's more part of the development environment than the deployment environment.
I use buildapp to deploy CL programs, and there's a way for Quicklisp to export its index of systems and buildapp to load it.
There's certainly room for improvement.

How does one avoid loading multiple asdf files for a common lisp project?

While working through Peter Seibel's book Practical Common Lisp, I've had some difficulty understanding how to handle the Common Lisp package system in conjunction with Emacs's SLIME and quicklisp. The project he supplies has the following structure:
spam.lisp in the package com.gigamonkeys.spam, which relies on two other packages, cl-ppcre and com.gigamonkeys.pathnames.
pathnames.lisp in the package com.gigamonkeys.pathnames
spam.asd, which describes the dependencies of com.gigamonkeys.spam package
pathnames.asd, which describes the dependencies of the com.gigamonkeys.pathnames package
The only way that I've currently found to build the final target, spam.lisp, is to:
Compile and load the pathnames.asd file using SLIME (C-x C-k)
Load the com.gigamonkeys.pathname package by typing (asdf:operate 'asdf:load-op 'spam) at the REPL
Load the cl-ppcre package by typing (ql:quickload "cl-ppcre") at the REPL
Compile and load the spam.asd file using SLIME
Load the com.gigamonkeys.spam package by typing (asdf:operate 'asdf:load-op 'spam) at the REPL
This seems like an insane amount of work required to use the functions defined in a single file (spam.lisp) - I've got to be doing something wrong. Is there some way to load spam.lisp and, recursively, its dependencies, with fewer commands?
Source + Full Story
Long story short, packages are bags of symbols and systems describe
relationships between files and other systems.
So, using your structure here is a simple system definition:
(asdf:defsystem #:spam-filter
:serial t
:description "Simple Spam Filter"
:author "PCL"
:depends-on (#:cl-ppcre
#:com.gigamonkeys.pathnames)
:components ((:file "package")
(:file "spam")))
Now, I'll suppose the system com.gigamonkeys.pathnames is in
~/src/lisp/com/gigamonkeys/pathnames/ and the spam filter in
~/src/lisp/spam-filter/.
M-xslimeRET
,cd~/src/lisp/com/gigamonkeys/pathnamesRET
(push (truename ".") asdf:*central-registry*)
,cd~/src/lisp/spam-filterRET
(push (truename ".") asdf:*central-registry*)
(ql:quickload :spam-filter)
Because Quicklisp relies on ASDF to load systems, you've to add the systems directories
in ASDF search-path.
You could also put your systems in Quicklisp's search path by adding
them in ~/quicklisp/local-projects/. You can either store your code
there or use symbolic links.
PS: You can find
here
a slime-shortcut that allows me to write ,addRET
instead of (push (truename ".") asdf:*central-registry*).
And just below another slime-shortcut to quickload a system.
(,qlsome-system-nameRET)
A little bit terminology:
a Package is a Common Lisp standard feature. A package is a namespace for symbols. Nothing more. It is not a file, not a module, not a library and not tied to any such thing. It's not even a namespace for functions, variables or classes. Just for symbols. There is a convention to put the definitions that setup a package into a file of its own. But that is just a convention.
a System is used to define, load and compile all files which make up a software library or application. The idea of a System is old (over thirty years). ASDF (Another System Definition Facility) is a popular and free implementation of that idea. Kent Pitman wrote in 1984 about The Description of Large Systems.
A part of such a system facility is a registry, from where all systems can be found. A system definition describes the files it consists of and all the systems it depends on. Typically such a registry is a directory in the file system and/or a registry (a variable, ...) in a running Lisp.
On loading a particular system, the system tool looks into the registry and finds it, loads the definition, loads recursively all needed systems, and then loads all the files.
See the ASDF manual: Configuring ASDF to find your systems

Resources