How do I use the live-code feature in sbcl? - common-lisp

I am trying to get live-coding to work in lisp. i have the file t.cl which contains only this line: (loop(write(- 2 1))). Now, when i run the file in bash with sbcl --load t.cl --eval '(quit)', it runs the line, but when I try to edit the file in another terminal and save it while it runs, nothing changes ..

Why your example fails
When running sbcl --load t.cl --eval '(quit)' in a shell, what this does is spin-up a SBCL Lisp image in a process, compile the file and run it. You then modify the file and save it to your disk. This last action is of no concern to the already running SBCL process, which has already compiled the previous file. SBCL read the file once when you asked it to, once it has the compiled instructions to run, it has no reason to look at the file again unless you explicitly ask it to.
A 'live' example with Emacs+SLIME
In order to perform 'live' changes to your running program, you must interact with the already running Lisp image. This is easily doable with Emacs+Slime. You can, for example, have a loop like so:
(defun foo (x) (+ x 3))
(dotimes (it 20)
(format t "~A~%" (foo it))
(sleep 1))
and then recompile foo during execution within the REPL with a new definition:
(defun foo (x) (+ x 100))
Another thread will be used to recompile the function. The new function will be used for future calls as soon as its compilation is finished.
The output in the REPL will look like:
3
4
5
CL-USER> (defun foo (x) (+ x 100))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
103
104
105
...
This would also work with the new definition of foo being compiled from another file as opposed to being entered directly in the REPL.
Working from the system shell
While you can already use the example above for development purposes, you might want to interact with a running SBCL Lisp image from the shell. I am not aware of how to do that. For your exact example, you want to get SBCL to reload eventual files that you have modified. A brief look at the SBCL manual doesn't seem to provide ways to pipe lisp code to an already running SBCL process.

Related

Changing the dir errors and hunchentoot - Dir not found?

I use a macro called use-db and change-to-path in a lot of my projects.
(defmacro use-db (db project-name &body query)
`(progn
(change-to-path ,project-name)
(clsql:connect ,db :database-type :sqlite3)
(unwind-protect (progn ,#query)
(clsql:disconnect :database ,db))))
(defmacro change-to-path (project-name)
`(uiop:chdir (merge-pathnames (write-to-string ,project-name) "/Users/vince/quicklisp/local-projects/")))
As you can see, change to path changes the repl dir. Then use-db reads the sqlite db from the root of the project folder. This works fine.
However, when I try to read a db as part of a route (easy-routes + hunchentoot):
(defroute test-file ("/results" :method :post)
()
(get-one-col-as-list-db #'first "firstname"))
(defun get-one-col-as-list-db (fn tablename)
(ki:use-db "new.db" 'custom-sender
(mapcar fn
(clsql:query
(concatenate 'string "Select * from " tablename)))))
I get the following error:
Error in SB-POSIX:CHDIR: No such file or directory (2)
without using the change-to-path macro, clsql function would note find the database.
Is there a better way to use sqlite (from the file system) and manage the pathing for specific db files?
To change the current working directory, temporarily, use uiop:with-current-directory:
(uiop:with-current-directory ("/tmp/")
(uiop:getcwd))
(edit) chdir has a side effect: it effectively changes the current working directory (uiop:getcwd), while with-current-directory doesn't, or it does but only temporarily for the code under it. For your sanity you want to avoid chdir's side effets (or I do, I want to work at a project root). It could have an effect on bare-bones Hunchentoot, if it looks at a www/ directory under the project root, but in your case I'm not sure.
Your use-db mimics a well-known pattern: get a resource, and be sure to release it in case of errors. These macros are often named "with-something", just like "with-current-directory". Does CLSQL have such a macro? It does: with-database.
Why is your change-to-path a macro? It can be a function that concatenates strings and calls chdir.
Inside use-db, "project-name" is an unknown variable. Look at the compiler warnings:
; caught WARNING:
; undefined variable: COMMON-LISP-USER::PROJECT-NAME
How do you use the two anyways?
Error in SB-POSIX:CHDIR: No such file or directory (2) […] without using the change-to-path macro
what is the directory it tries to go to? You'd have a tip on how things are not working.
I try to never rely on the process working directory, because from Common Lisp I am much happier using *default-pathname-defaults* (no side effects!). This is a special variable with a long name, and what I want is to use merge-pathnames in most of the cases, so I have this macro:
(defmacro with-path (pathname &body body)
`(let ((*default-pathname-defaults* (merge-pathnames ,pathname)))
,#body))
It is just a local binding of a special variable so unwinding is trivial, and it comes with all the benefits of CL pathnames (and some of its drawbacks but that's fine).
On the Posix side of things, programs want a native namestring so I also use this a lot:
(defun fullname (pathname)
(osicat:native-namestring (merge-pathnames pathname)))
I do also have a concept of projects in my environment so it looks as follows. Note that I try to carry the special variable until the last moment, until it needs to be given to other processes, like:
(within-project projects:my-project
(with-path "build/"
(run "make")))
Here run grabs the current *default-pathname-defaults* for its :directory argument (using sb-ext:run-program but this is a detail).
Since it is a pathname, some components can be wild too, if that makes sense in your use case. For example, you can avoid giving a ".db" suffix if you prefer:
(defmacro using-db ((name db) &body body)
`(with-path #P"*.db"
(with-database (,name (list (fullname ,db)) :database-type :sqlite3)
,#body)))
For example, here I enter the "/tmp/" directory and open database "test.db":
(with-path "/tmp/"
(using-db (database "test")
database)))
#<CLSQL-SQLITE3:SQLITE3-DATABASE /tmp/test.db CLOSED {100DD6CE63}>
Edit: with respect to the following code:
(uiop:chdir
(merge-pathnames (write-to-string project-name)
"/Users/vince/quicklisp/local-projects/")))
Note that you can use ASDF to access files relative to systems:
(asdf:system-relative-pathname :cl-ppcre "ppcre.lisp")
#P"..../quicklisp/software/cl-ppcre-20220220-git/ppcre.lisp"

How to load an asdf system silently (without using quicklisp)? [duplicate]

I have created a small project using Steel Bank Common Lisp and I am using ASDF to compile and load it. The load command is:
(asdf:load-system :<my-system>)
Everything works fine (the program gets compiled and runs fine) but I keep getting output like
; compiling file "[...].lisp" (written 13 APR 2014 06:20:03 PM):
; compiling (DEFPACKAGE :<my-package> ...)
; compiling (DEFUN <my-func-1> ...)
; compiling (DEFUN <my-func-2> ...)
; compiling (DEFUN <my-func-3> ...)
each time the program is recompiled.
I suppose this output comes from asdf because I am invoking the application with
sbcl --noinform --noprint --script runner_sbcl.lisp
where runner_sbcl.lisp loads the actual application via asdf:load-system. So I suppose
this output does not come from sbcl.
Is there any way to disable console output in asdf:load-system? I would like to be only notified about compilation errors / warnings.
I could not find any information in the documentation.
what about
(setf *load-verbose* nil)
(setf *load-print* nil)
(setf *compile-verbose* nil)
(setf *compile-print* nil)
?
Why are they set, anyway?
This is my workaround for a similar issue I had with quicklisp's ql:quickload.
(with-output-to-string (*standard-output*)
;; asdf:load-system or ql:quickload..
(asdf:load-system :<your-system>))

Running Rdevtool::test_that on buffer save in ESS

My workflow:
Hack away at an .R or test_*.R file
Save buffer.
Switch to window with *R* process
hit C-p, RET. to re-evaluate devtools::test()
To me, this seems far too arduous. Why can't I get R to run devtools::test() automatically when I save the buffer? Please help, my fingers can barely take the strain of the seven extra keystrokes!
This worked for a similar setup (switching to a shell buffer):
(defun devtools-test ()
(interactive)
(when (string-match (rx-to-string `(: ".R" eos) t) (buffer-name))
(switch-to-buffer "*R*")
(end-of-buffer)
(insert "devtools::test()")
(comint-send-input)))
(add-hook 'after-save-hook 'devtools-test)
Does that work for you?
For completeness, there are easier way to do this in recent versions of ESS.
The function/command ess-r-devtools-test now runs devtools::test.
So you could achieve this with (untested):
(add-hook 'ess-r-mode-hook
(lambda ()
(add-hook 'after-save-hook 'devtools-test nil 'local)))
In addition, there are many other useful functions under ess-r-devtools-* for build, install etc.
It's worth noting that calling ess-r-devtools-test with the universal argument will filter tests by the current file.
So calling C-u M-x ess-r-devtools from a file my-file.R will run devtools::test(filter="my-file").
This can be useful to bear in mind when choosing names for test files, or when rerunning only the current test file.
There is a ess-eval-linewise function which you can use.
Something like this (not tested):
(defun devtools-test ()
(interactive)
(when (and (equal ess-dialect "R")
(string-match "^test.*\\.[Rr]$" (buffer-name)))
(ess-eval-linewise "devtools::test()")))
(add-hook 'after-save-hook 'devtools-test)
I would not recommend this though, as for some packages tests take quite a while to run. You don't want them running on every save.
There will be a dedicated devtools functionality in ess soon. Follow this issue.

CL and SWIG: working example?

Doing a SWIG tutorial, and using the example.c, example.i as they provided there. I generated lisp file with swig -cffi example.i.
But when I run test.lisp with SBCL, i get a complaint about undefined alien function, as well as complaints when compiling the example.lisp itself. I'm pretty sure I still have to compile my example.c into a library and then somehow tell SBCL to load it! But the docs are very scant on this, except for this.
Can somebody tell me how to do this or is there a better way than SWIG to automatically generate CFFI bindings from C/C++ sources??
sbcl output:
...
;
; caught STYLE-WARNING:
; Undefined alien: "fact"
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
;
; caught STYLE-WARNING:
; Undefined alien: "my_mod"
...
test.lisp
;call C functions defined in example.c
(require :cffi)
;;;(require :example "example.lisp")
(load "example.lisp")
(fact 2)
(quit)
First, you need to compile the C library. Do something like:
gcc -shared example.c -o libexample.so
Of course, for a complex existing library compilation could be considerably more complex -- if you're wrapping an existing library, it probably comes with some sort of Makefile to help you build it.
Then, in Lisp, use CFFI to define and load the library. This seems to be the main part that you're missing.
(cffi:define-foreign-library libexample
(t (:default "libexample"))) ;; note no .so suffix here
(cffi:use-foreign-library libexample)
This part:
(t (:default "libexample"))
is a conditional which you can use to give different loading instructions for different platforms. (t ...) is the catchall option, much like with COND. You can find the exact syntax in the documentation for define-foreign-library.
You would now normally use cffi:defcfun and so on to define the functions in the library. This is what the SWIG-generated file does for you, so load it:
(load "example.lisp")
You can now call the functions as normal Lisp functions:
(fact 5)
=> 120

ESS: ess-request-a-process defaults to "S"

Quite often I find myself with bunch of R processes running in ESS buffers. There's a convenient Lisp function ess-request-a-process that asks for R process, and brings it to front. The only downside is that it somehow defaults to S, so each time I'm about to make a switch, I have to type R, ad nauseam.
I tried customising the ess-language variable, but even if I set value to "R", i.e. 4 for current session, or even if I save settings for future session, as soon as I type C-c C-k, automagically S appears once again. It's very annoying, and I really don't want to end up with C-x C-b and then C-s for desired R session! =)
I even tried setting (setq-default ess-language "R") in .emacs, but with no luck...
BTW, I'm running Emacs v. 23.1.1 on Linux Mint and Emacs v. 23.2 on Arch Linux, with ESS v. 5.12. If that's relevant, I run Emacs from terminal with -nw argument. Here's my .emacs:
;; start server
(server-start)
;; load ESS
(require 'ess-site)
(require 'ess-rutils)
;; set HTML help as default
(setq inferior-ess-r-help-command "help(\"%s\", help_type = \"html\")\n")
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(ess-help-kill-bogus-buffers t)
'(ess-rutils-keys nil)
'(show-paren-mode t))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
(put 'upcase-region 'disabled nil)
So... how to set R once and for all? (I don't use S/S+/SAS)
I did not know about this function so far. C-c C-k is bound to ess-force-buffer-current in ESS buffers.
[edit: C-c C-k is indeed bound to ess-request-a-process in iESS, in ESS it's ess-force-buffer-current]
In any case the variable you have to customize is ess-dialect
(setq-default ess-dialect "R")
It's buffer-local variable and some other stuff in ess-mode-hook might set it a different value.
Check it in each buffer with C-h v ess-dialect
Additionally, if you already running several processes then ess-switch-process (C-c C-s) might be the right way to go.
[edit: it will not jump to a process but just reset the associated process of the current ESS buffer]
[edit: After dwelling deeper on the issue it turned out that ess-request-a-process uses ess-language variable were the ess-dialect seems to be more appropriate. The problem is that each time an ess-inferior process starts it resets the global value of ess-language. This is why setting it in your case didn't work.
Here is a quick fix:
(defun ess-set-language ()
(setq-default ess-language "R")
(setq ess-language "R")
)
(add-hook 'ess-post-run-hook 'ess-set-language t)
]

Resources