Up to now in my Lisp adventures I've just been pasting functions as written in a code editor into the REPL to run them, but I now have a program of sufficient size to build on that it will be convenient to use (load "filename.lisp") for the first time in my workflow.
Must I start using packages and/or namespacing to achieve this?
I find that when I use load, as above, I get
** - Continuable Error
DEFUN/DEFMACRO(CLASS): #<PACKAGE CLOS> is locked
If you continue (by typing 'continue'): Ignore the lock and proceed
The following restarts are also available:
SKIP :R1 skip (DEFMACRO CLASS # ...)
RETRY :R2 retry (DEFMACRO CLASS # ...)
STOP :R3 stop loading file /Users/m/cl/ansi-cl/ch17-objects/177d-new-full.lisp
ABORT :R4 Abort main loop
My .lisp file contains a macro called class, so I understand the error, sort of.
The thing is, when I paste the contents of the file directly into the REPL, I get no such error.
What's causing the difference in behaviour?
Is it packages, namespaces or something else?
I can indeed just type continue, and the file will load, but I'd like to understand what is happening here; what's the cause of this "Continuable error", and how should I deal with it if at all?
Package locking in CLISP is explained in the manual.
Symbol class is an ANSI CL symbol, so it cannot name any user-defined entity, and thus your program is not conforming, as explained in 11.1.2.1.2 Constraints on the COMMON-LISP Package for Conforming Programs.
You should rename your macro.
The lack of the error in the REPL is a bug in homebrew clisp.
CLISP as distributed with ubuntu works correctly.
When I build CLISP from sources on Mac, it works correctly too.
Related
Firstly, my background in programming is quite basic Python so Sage and everything that comes with it is very new to me, so apologies if I mix up terminology etc.
I am currently using an online version of LiE to assist me in performing some algebra based computations, but as the computations are getting quite complex I am hoping to move from pen and paper to sage math to perform these calculations for me.
I installed Sage and when I open SageMath notebook I am taken to Jupyter (which I guess is an IDE).
My issue is when I use Jupyter and try to run something like "lie.diagram('A2') I am presented with a bunch of errors which I do not understand.
I then tried a different interpreter, namely GAP, and this works fine e.g. if I input "gap(34).Factors()" then Jupyter will output "[2, 17]".
I checked the path "/opt/sagemath-9.2/local/lib/python3.7/site-packages/sage/interfaces/" and both "gap.py" and "lie.py" are there, so I am unsure why gap works and lie does not.
Lastly, if I use the sage cell server and input "lie.diagram('A2')" then it does output the required diagram, so my guess is there is some behind the scenes issue between LiE and Jupyter specifically.
Thanks!
In SageMath, GAP is a standard package, while
LiE is an optional package. This means when you
install Sage, you get GAP but you don't get LiE
unless you specifically ask for it.
On SageCell and CoCalc, the Sage installation
is very complete, with many optional packages
installed, so calling LiE works there.
The files in .../sage/interfaces/ implement
the interfaces between Sage and other software,
and they are present whether or not the other
software is installed.
In your case, the interface to LiE exists,
but LiE is not installed, so when you run
a command such as lie.diagram('A2'),Sage
tries to talk to LiE using its LiE interface,
but does not find LiE and reports an error
which in my case looks like:
EOF ... Traceback (most recent call last)
...
EOF: End Of File (EOF). Empty string style platform.
...
During handling of the above exception, another exception occurred:
RuntimeError ... Traceback (most recent call last)
...
RuntimeError: unable to start LiE: End Of File (EOF). Empty string style platform.
...
During handling of the above exception, another exception occurred:
TypeError ... Traceback (most recent call last)
...
TypeError: unable to start LiE: End Of File (EOF). Empty string style platform.
...
To install LiE, if you built Sage from source,
open a terminal and run these command:
cd /opt/sagemath-9.2
make -s V=0 configure
./configure --enable-lie -q
make -s V=0
Next time you start Sage, you can use LiE:
sage: lie.diagram('A2')
O---O
1 2
A2
In some situations, e.g. when using Eva's abstract domains, loading a session always emits a warning of the form:
[kernel] Warning: 1 state in saved file ignored. It is invalid in this Frama-C configuration.
For instance, here's how to reproduce it with a simple C file containing nothing but a main function (int main() {}):
frama-c main.c -eva -eva-domains gauges -save framac.sav
frama-c -load framac.sav
The warning also appears when using the template generated by frama-c-script make-template and enabling an abstract domain in EVAFLAGS.
The warning about ignoring states in saved files is mostly harmless. It simply means that Frama-C had (during the -save) some data that is not used in the current configuration (-load). There are two main situations in which this can happen:
When loading a state with less plug-ins; for instance, if before the -save you loaded some external plug-in, but not before the -load;
When using Eva's alternative domains with memexec (on by default). Eva instantiates a functor with the enabled domains at each execution, and this functor is saved in the session file. When loading it later, it will emit the message, which can be safely ignored.
Another situation which might lead to this warning would be if you recompiled Frama-C after saving a session, and reloaded it with a different binary. Sessions are not portable among different versions, so a version check prevents this from happening accidentally, but when recompiling yourself without changing the version number, this might happen. In that case, the warning should draw attention to the fact, and regenerating the session file should eliminate it.
Context
I'm developing (for the first time) an R package using Rcpp which implements an interface to another program (maxima). This package defines a C++ class, whose constructor needs to retrieve the path to an initialization script that gets installed with the package (the in-package path is inst/extdata/maxima-init.mac. The path to this script is then used as a parameter to spawn a child process that runs the program.
In order to retrieve the path to the installed initialization script I'm calling the R function base::system.file from within the C++ class constructor definition:
...
Environment env("package:base");
Function f = env["system.file"];
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
...
# spawn child process using path in utilsDir
My R/zzz.R creates an object of that class when the packages gets attached:
loadModule("Maxima", TRUE)
.onAttach <- function(libname, pkgname) {
"package:base" %in% search()
maxima <<- new(RMaxima)
}
The Problem
I can install.packages(rmaxima) and library(rmaxima) just fine and the package works as expected.
I now want to increase my development efficiency by using devtools::load_all() to avoid having to R CMD build rmaxima, install.packages(rmaxima) and library(rmaxima) each time I want to test changes. However, when calling devtools::load_all() (or similarily devtools::test() (working directory set to package root) my implementation freezes, because the variable utilsDir is empty and therefore the process launching does not return (I guess it keeps waiting for a valid path). I eventually need to manually kill the process. The same thing happens without setting .onAttach()
Apparently devtools::load_all() does not resemble R's default search path on restart. What can I do? Is this the problem or am I missing something else?
Update
I just came across the following notion of in the devtools::load_all() R documentation file which could be a tip in the right direction
Shim files:
‘load_all’ also inserts shim functions into the imports environment of
the loaded package. It presently adds a replacement version of
‘system.file’ which returns different paths from ‘base::system.file’.
This is needed because installed and uninstalled package sources have
different directory structures. Note that this is not a perfect
replacement for base::system.file.
Also I realized, that devtools::load_all() only temporarily installs my package into, but somehow doesn't the files from my inst/
rcst#Velveeta:~$ ls -1R /tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/:
DESCRIPTION
libs
Meta
NAMESPACE
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/libs:
rmaxima.so
/tmp/RtmpdnvOQg/devtools_install_ee1e82c780/rmaxima/Meta:
features.rds
package.rds
As it turns out devtools provides a solution to exactly this problem.
In short: calling system.file (i.e. from the global environitment and having the devtools package attached) solves the issue. Specifically the modification:
// Environment env("package:base");
// Function f = env["system.file"];
Function f("system.file");
fs::path p(Rcpp::as<std::string>(f("extdata", "maxima-init.mac", Named("package") = "rmaxima")));
std::string utilsDir = p.parent_path().string();
Explanation
base::system.file(..., mustWork = FALSE) returns an empty string if no match is found. devtools::load_all() temporarily installs the packages inside /tmp/ (on my linux machine). The directory structure of the temporary installation differs from the one of the regular installation, i.e. the one created by install.packages(). In my case, most notably, devtools::load_all() does not copy the inst/ directory, which contains the initialization file.
Now, calling base::system.file("maxima-init.mac", package="rmaxima", mustWork=FALSE) naturally fails, since it searches inside the temporary installation. Having devtools attached masks system.file() with devtools::system.file(), which as mentioned above is "... meant to intercept calls to base::sysem.file() " and behaves differently from base::system.file(). Practically, I think this means, that it will search for the package's source directory instead of the temporary installation.
This way, simply calling system.file() from the global environment calls the right function, either from devtools or base, for either the development or user version of the package automatically.
Nonetheless, using ccache additionally (thanks #dirk) substantially speeds up my development workflow.
While using SBCL normally I want to try CCL for some testing and installed it via homebrew on my computer. That worked fine but I fail to use quicklisp with CCL. If I try to load quicklisp's setup.lisp I get the following error message:
➜ ~ ccl64
Clozure Common Lisp Version 1.11.6 DarwinX8664
For more information about CCL, please see http://ccl.clozure.com.
CCL is free software. It is distributed under the terms of the Apache
Licence, Version 2.0.
? (require 'asdf)
ASDF
("uiop" "UIOP" "asdf" "ASDF")
? (load "~/quicklisp/setup.lisp")
> Error: There is no package named "ASDF/SYSTEM-REGISTRY" .
> While executing: CCL::%FASL-NVPACKAGE, in process listener(1).
> Type :GO to continue, :POP to abort, :R for a list of available restarts.
> If continued: Retry finding package with name "ASDF/SYSTEM-REGISTRY".
> Type :? for other options.
1 > :POP
I couldn't find a simple solution searching the web. CCL comes with ASDF as (require 'ASDF) is working. In quicklisp's documentation I couldn't find anything about extra efforts to use it with two lisp implementations in parallel.
If I check ASDF's version as suggested here I get "3.1.5" as installed version, which should be quite recent.
Is there anything obvious I am missing?
I think I found it: I had some old data in my ~/.cache directory from another attempt to install ccl. After deleting it, I can load quicklisp's setup.lisp without any error.
Please advice If I should delete my question or leave it here to prevent others from repeating my errors.
Please note that various Lisps have some startup files. Some of them are by default:
SBCL: ~/.sbclrc
CLISP: ~/.clisprc.lisp
Clozure: ~/.ccl-init.lisp
From the documentation of CCL:
By default, Clozure CL will look for a file named ccl-init.lisp in your home directory, and load it upon startup. On Unix systems, it will also look for .ccl-init.lisp.
CCL uses an ordinary lisp file called ccl-init.lisp which on unix systems is usually put on your home path. So you can add the following lines in that file (on my Ubuntu machine it's path is /home/me/.ccl-init.lisp) which instructs CCL to load quicklisp upon startup:
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
This is enough for enabling your CCL to communicate with quicklisp.
If I want to create a Lisp-image of my program, how do I do it properly? Are there any prerequisites? And doesn't it play nicely with QUICKLISP?
Right now, if I start SBCL (with just QUICKLISP pre-loaded) and save the image:
(save-lisp-and-die "core")
And then try to start SBCL again with this image
sbcl --core core
And then try to do:
(ql:quickload :cl-yaclyaml)
I get the following:
To load "cl-yaclyaml":
Load 1 ASDF system:
cl-yaclyaml
; Loading "cl-yaclyaml"
.......
debugger invoked on a SB-INT:EXTENSION-FAILURE in thread
#<THREAD "main thread" RUNNING {100322C613}>:
Don't know how to REQUIRE sb-sprof.
See also:
The SBCL Manual, Variable *MODULE-PROVIDER-FUNCTIONS*
The SBCL Manual, Function REQUIRE
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [RETRY ] Retry completing load for #<REQUIRE-SYSTEM "sb-sprof">.
1: [ACCEPT ] Continue, treating completing load for #<REQUIRE-SYSTEM "sb-sprof"> as having been successful.
2: Retry ASDF operation.
3: [CLEAR-CONFIGURATION-AND-RETRY] Retry ASDF operation after resetting the configuration.
4: [ABORT ] Give up on "cl-yaclyaml"
5: Exit debugger, returning to top level.
(SB-IMPL::REQUIRE-ERROR "Don't know how to ~S ~A." REQUIRE "sb-sprof")
0]
Alternatively, if I try:
(require 'sb-sprof)
when sbcl is started with saved core, I get the same error. If sbcl is started just as sbcl there is no error reported.
In fact, pre-loading QUICKLISP is not a problem: the same problem happens if sbcl is called initially with sbcl --no-userinit --no-sysinit.
Am I doing it wrong?
PS. If I use roswell, ros -L sbcl-bin -m core run somehow doesn't pick up the image (tested by declaring variable *A* before saving and not seeing it once restarted).
PS2. So far what it looks like is that sbcl does not provide extension modules (SB-SPROF, SB-POSIX, etc.) unless they are explicitly required prior saving the image.
Thanks for the help from #jkiiski here is the full explanation and solution:
SBCL uses extra modules (SB-SPROF, SB-POSIX and others) that are not always loaded into the image. These module reside in contrib directory located either where SBCL_HOME environment variable pointing (if it is set) or where the image resides (for example, in /usr/local/lib/sbcl/).
When an image is saved in another location and if SBCL_HOME is not set, SBCL won't be able to find contrib, hence the errors that I saw.
Setting SBCL_HOME to point to contrib location (or copying contrib to image location or new image to contrib location) solves the problem.
Finally, about roswell: roswell parameter -m searches for images in a specific location. For SBCL (sbcl-bin) it would be something like ~/.roswell/impls/x86-64/linux/sbcl-bin/1.3.7/dump/. Secondly, the image name for SBCL must have the form <name>.core. And to start it, use: ros -m <name> -L sbcl-bin run. (Quick edit: better use ros dump for saving images using roswell as it was pointed out to me)
If you want to create executables, you could try the following:
(sb-ext:save-lisp-and-die
"core"
:compression t
;; this is the main function:
:toplevel (lambda ()
(print "hell world")
0)
:executable t)
With this you should be able to call QUICKLOAD as you wish. Maybe you want to checkout my extension to CL-PROJECT for creating executables: https://github.com/ritschmaster/cl-project