Recently, I have come across the so-called hash-dot Common Lisp reader macro and am wondering how it works and what it does. Using search engines was not of much help, so any examples, explanations and especially uses-cases are most welcome.
In the spec this is called sharpsign dot. It does read-time evaluation. You can search the Common Lisp hyper spec for that. I don’t have it to hand but I believe in Emacs with slime one can look up the documentation for reader macros. Do C-c C-d C-h to see if there is a command for that.
#.foo reads as whatever (eval foo) returns. Thus:
CL-USER> '((+ 1 2) #.(+ 1 2))
((+ 1 2) 3)
CL-USER> (read)
#.(* 3 4)
12
Related
I am trying to learn Common Lisp with the book Common Lisp: A gentle introduction to Symbolic Computation. In addition, I am using SBCL, Emacs, and Slime.
By the end of chapter 9, the author shows the dribble tool.
He shows the following:
I tried to reproduce the commands presented by the author. Considering the inputs, the only difference was the fact that I put a different location to save the file. In my environment, I did:
CL-USER> (dribble "/home/pedro/miscellaneous/misc/symbolic-computation/teste-tool.log")
; No value
CL-USER> (cons 2 nil)
(2)
CL-USER> '(is driblle really working?)
(IS DRIBLLE REALLY WORKING?)
CL-USER> "is dribble useful at all?"
"is dribble useful at all?"
CL-USER> (dribble)
; No value
The file was indeed created:
$ readlink -f teste-tool.log
/home/pedro/miscellaneous/misc/symbolic-computation/teste-tool.log
Note that I did not get messages such as "Now recording in file --location---" in the REPL while I was typing. But this may vary according to the Lisp implementation.
The big surprise was that, unfortunately, the file was empty. Thus, dribble did not work as expected.
Did I do something wrong?
Yes, by default, within Slime I don't think this works.
It will work within the SBCL Repl:
➜ sbcl
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (dribble "dribble-test.lisp")
* (* 8 5)
40
* "Will this work?"
"Will this work?"
* (dribble)
* %
Which can be confirmed:
➜ cat dribble-test.lisp
* (* 8 5)
40
* "Will this work?"
"Will this work?"
* (dribble)
Saving "REPL history" seems less useful with Slime IMO because of all the "non-REPL evaluation" that you do by e.g. selecting a function or an expression or region and evaluating that in the REPL.
To actually save and view history within Slime, see slime-repl-save-history and associated functions; you can even merge histories from independent Repls if you so choose :-)
I'm reviving an old LISP program from the early 1980s.
(It's the Nelson-Oppen simplifier, an early proof system.
This version was part of the Ford Pascal-F Verifier,
and was running in Franz LISP in 1982.) Here's
the entire program:
https://github.com/John-Nagle/pasv/tree/master/src/CPC4
I'm converting the code to run under clisp on Linux,
and need some advice. Most of the problems are with
macros.
HUNKSHELL
Hunkshell was a 1970s Stanford SAIL hack to support records with named fields in LISP. I think I've converted this OK; it seems to work.
https://github.com/John-Nagle/pasv/blob/master/src/CPC4/hunkshell.l
The original macro generated more macros as record update functions.
I'm generating defuns. Is there any reason to generate macros?
By the way, look at what I wrote for "CONCAT". Is there a better way
to do that?
DEFMAC
More old SAIL macros, to make macro definition easier before defmacro became part of the language.
https://github.com/John-Nagle/pasv/blob/master/src/CPC4/defmac.l
I've been struggling with "defunobj". Here's my CL version, partly converted:
; This macro works just like defun, except that both the value and the
; function-binding of the symbol being defined are set to the function
; being defined. Therefore, after (defunobj f ...), (f ...) calls the
; defined function, whereas f evaluates to the function itself.
;
(defmacro defunobj (fname args &rest b)
`(progn
(defun ,fname ,args ,b)
;;;;(declare (special ,fname)) ;;;; ***declare not allowed here
(setq ,fname (getd ',fname))))
If I made that declare a proclaim, would that work right?
And what replaces getd to get a function pointer?
SPECIAL
There are lots of (DECLARE (SPECIAL FOO)) declarations at top
level in this code. That's not allowed in CL. Is it
appropriate to use (PROCLAIM (SPECIAL FOO)) instead?
Concat
Essentially correct, but indentation is broken (like everywhere else - I suggest Emacs to fix it).
Also, you don't need the values there.
defunobj
I suggest defparameter instead
of setq. Generally speaking, before setting a variable (with, e.g., setq) one should establish it (with, e.g., let or defvar).
fdefinition is what
you are looking for instead of getd.
I also don't think you are using backquote right:
(defmacro defunobj (fname &body body)
`(progn
(defun ,fname ,#body)
(defparameter ,fname (fdefinition ',fname))))
Special
I think defvar and defparameter
are better than proclaim
special.
PS. Are you aware of the CR.se site?
This question already has answers here:
operator #+ and #- in .sbclrc
(2 answers)
Closed 6 years ago.
Recently I tried to read code about cl-mysql, but got stuck with the #+.
Tried to google it, but not work, so turn to here
(defun make-lock (name)
#+sb-thread (sb-thread:make-mutex :name name)
#+ecl (mp:make-lock :name name)
#+armedbear (ext:make-thread-lock)
#+ (and clisp mt) (mt:make-mutex :name name)
#+allegro (mp:make-process-lock :name name))
And looks like it is for different backend lisp compiler. But still no idea why write something like this.
Anyone can help me make it clear, thx.
#+ is a reader-macro that checks if a keyword is in the special variable *FEATURES*. If it isn't there, the following form will be skipped over (by the reader; the compiler will never see it). There is also #- which does the opposite.
There are some things that aren't part of the Common Lisp standard, but are important enough that all (or most) implementations provide a non-standard extension for them. When you want to use them in code that needs to work on multiple implementations, you have to use read-time conditionals to provide the correct code for the current implementation. Mutexes (and threads in general) are one of those things.
Of course there may be features provided by third party libraries as well. The contents of *FEATURES* will look something like this:
(:SWANK :QUICKLISP :SB-BSD-SOCKETS-ADDRINFO :ASDF-PACKAGE-SYSTEM :ASDF3.1
:ASDF3 :ASDF2 :ASDF :OS-UNIX :NON-BASE-CHARS-EXIST-P :ASDF-UNICODE :64-BIT
:64-BIT-REGISTERS :ALIEN-CALLBACKS :ANSI-CL :ASH-RIGHT-VOPS
:C-STACK-IS-CONTROL-STACK :COMMON-LISP :COMPARE-AND-SWAP-VOPS
:COMPLEX-FLOAT-VOPS :CYCLE-COUNTER :ELF :FLOAT-EQL-VOPS
:FP-AND-PC-STANDARD-SAVE :GENCGC :IEEE-FLOATING-POINT :INLINE-CONSTANTS
:INTEGER-EQL-VOP :INTERLEAVED-RAW-SLOTS :LARGEFILE :LINKAGE-TABLE :LINUX
:LITTLE-ENDIAN :MEMORY-BARRIER-VOPS :MULTIPLY-HIGH-VOPS :OS-PROVIDES-DLADDR
:OS-PROVIDES-DLOPEN :OS-PROVIDES-GETPROTOBY-R :OS-PROVIDES-POLL
:OS-PROVIDES-PUTWC :OS-PROVIDES-SUSECONDS-T :PACKAGE-LOCAL-NICKNAMES
:PRECISE-ARG-COUNT-ERROR :RAW-INSTANCE-INIT-VOPS :SB-DOC :SB-EVAL :SB-FUTEX
:SB-LDB :SB-PACKAGE-LOCKS :SB-SIMD-PACK :SB-SOURCE-LOCATIONS :SB-TEST
:SB-THREAD :SB-UNICODE :SBCL :STACK-ALLOCATABLE-CLOSURES
:STACK-ALLOCATABLE-FIXED-OBJECTS :STACK-ALLOCATABLE-LISTS
:STACK-ALLOCATABLE-VECTORS :STACK-GROWS-DOWNWARD-NOT-UPWARD :SYMBOL-INFO-VOPS
:UNIX :UNWIND-TO-FRAME-AND-CALL-VOP :X86-64)
So if you wanted to write code that depends on Quicklisp for example, you could use #+quicklisp. If you wanted code that is only run if Quicklisp is not available, you'd use #-quicklisp.
You can also use a boolean expression of features. For example,
#+(or sbcl ecl) (format t "Foo!")
would print Foo! on either SBCL or ECL.
#+(and sbcl quicklisp) (format t "Bar!")
would only print Bar! on SBCL that has Quicklisp available.
One could imagine that we can write:
(defun make-lock (name)
(cond ((member :sb-thread *features)
(sb-thread:make-mutex :name name))
((member :ecl *features*)
(mp:make-lock :name name))
...))
But that does usually not work, because we can't read symbols when their package is not existing and some packages are implementation/library/application specific. Packages are not created at read time in a lazy/automatic fashion.
In Common Lisp, reading a symbol of a package, which does not exist, leads to an error:
CL-USER 1 > (read-from-string "foo:bar")
Error: Reader cannot find package FOO.
1 (continue) Create the FOO package.
2 Use another package instead of FOO.
3 Try finding package FOO again.
4 (abort) Return to level 0.
5 Return to top loop level 0.
In your example sb-thread:make-mutex is a symbol which makes sense in SBCL, but not in Allegro CL. Additionally the package SB-THREAD does not exist in Allegro CL. Thus Allegro CL needs to be protected from reading it. In this case, the symbol sb-thread:make-mutex will only be read, if the the feature sb-thread is present on the cl:*features* list. Which is likely only for SBCL, or a Lisp which claims to have sb-threads available.
The feature expressions here prevents the Lisp from trying to read symbols with unknown packages - the packages are unknown, because the respective software is not loaded or not available.
The other day (perhaps yesterday) I was quite perplexed about this #+nil read-time conditional found in https://github.com/billstclair/defperson/blob/master/defperson.lisp#L289.
After some deep thinking I came to the conclusion that this is very lispy way of commenting out code. Can someone confirm this?
Perhaps my assumptions are completely wrong. Anyway, thanks in advance.
Yes, it is a lispy way of commenting code, but you shouldn't leave this out in production code.
A better alternative is #+(or).
It only takes one more character, it takes the same key presses if you use Emacs paredit or some other mode that automatically inserts the closing parenthesis, and it's not subject to the existence of the symbol :nil in *features*.
See CLHS 2.4.8.17 Sharpsign Plus
To conditionalize reading expressions from input, Common Lisp uses feature expressions.
In this case it has been used to comment out a form.
It's a part of the reader. #+ looks if the next item, usually as a keyword symbol with the same name, is a member of the list *features*. If yes, the then next item is read as normal, if not, it is skipped.. Usually :NIL is not a member of that list, so the item is skipped. Thus it hides the expression from Lisp. There might have been a Lisp implementation, where this would not work : NIL, New Implementation of Lisp. It might have had the symbol :NIL on the *features* list, to indicate the name of the implementation.
Features like NIL are by default read in the keyword package:
#+NIL -> looks for :NIL in cl:*features*
#+CL:NIL -> looks for CL:NIL in cl:*features*
Example
(let ((string1 "#+nil foo bar")) ; string to read from
(print (read-from-string string1)) ; read from the string
(let ((*features* (cons :nil *features*))) ; add :NIL to *features*
(print (read-from-string string1))) ; read from the string
(values)) ; return no values
It prints:
BAR
FOO
Note that Common Lisp has other ways to comment out forms:
; (sin 3) should we use that?
#| (sin 3) should we use that?
(cos 3) or this? |#
I love the idea of image-based languages, and lately I've been toying with Common Lisp via sbcl. I've read in a few places about how through being able to save and load back an image of the virtual machine, you can evolve an application or set of apps running on that image.
I get how to load code into an image and get it running, slime makes this sort of thing very nice, but my question is this: How can I tell what functions are defined in an image? Let's say I want to make an update to a function days or months after it has been running and I can't remember the name. Is there any way to get to the code or even just the names of the functions defined in an the image?
Now, I do write the code out into source and load it in via the repl, so I have a copy there, but it seems like this would be an obvious feature.
Common Lisp has the idea of packages. Packages are kind of a registry for symbols and are used as namespaces for symbols. You can ask Common Lisp for a list of all packages.
CL-USER 1 > (list-all-packages)
(#<The SQL-COMMON package, 0/4 internal, 28/32 external>
#<The LOOP package, 245/256 internal, 3/4 external>
#<The COMM package, 0/4 internal, 940/1024 external>
#<The REG package, 41/64 internal, 0/4 external>
...)
Each packages stores the interned symbols in some data structure. You can ask Common Lisp which symbols are interned in a package.
CL-USER 2 > (loop for symbol being
each external-symbol in (find-package "COMMON-LISP")
collect symbol)
(MAKE-ARRAY INVOKE-DEBUGGER STRING-TRIM ...)
To make that easier, Common Lisp provides functions APROPOS and APROPOS-LIST.
CL-USER 3 > (apropos "MAKE-LOCK")
MP::INTERNAL-MAKE-LOCK (defined)
MP:MAKE-LOCK (defined)
WWW-UTILS:MAKE-LOCK (defined)
MAKE-LOCK
RESOURCES::MAKE-LOCK (defined)
MINIPROC:MAKE-LOCK (defined)
Functions, Classes, etc. use symbols as their identifier. You can also ask a symbol, which function it denotes.
CL-USER 4 > (symbol-function 'www-utils:make-lock)
#<Function WWW-UTILS:MAKE-LOCK 41E006A69C>
Sometimes a Common Lisp also records the definition of functions. Then the function FUNCTION-LAMBDA-EXPRESSION can be used to retrieve 'it'.
CL-USER 5 > (defun foo (a) (* (sin a) a))
FOO
CL-USER 6 > (pprint (function-lambda-expression 'foo))
(LAMBDA (A)
(DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 41403151C3>))
(DECLARE (LAMBDA-NAME FOO))
(* (SIN A) A))
But usually nowadays Common Lisp implementations don't use the recorded definitions, but record the locations of the source for each Lisp construct.
Most Common Lisp implementations can track the source locations in an implementation specific way.
The Common Lisp standard defines a function ED.
CL-USER 7 > (ed 'www-utils:make-lock)
This calls an editor (internal or external) and should open the source code for that function. To make that work, Common Lisp needs to keep track of the source location for each function. Next the editor needs to have access to that source. Sometimes the location recorded is an absolute path /Users/joswig/lisp/utils.lisp . If the editor wants to open that file, it should be accessible. But it is also possible to use logical pathnames like http:server;utils.lisp . This is then translated into a real physical pathname. This translation can later be configured. So it would be possible to move a Lisp to a different machine with different pathnames, configure the logical pathname HTTP and then the Lisp still finds all source code, even though it is on a different machine with a different file system structure. So, to make it work may need some configuration. But it is a very useful feature and it is widely used.
How the recording of source code and how the recording of source locations work is implementation dependent and is a feature of the respective Lisp in combination with its development environment. Better Lisp implementations have a lot of features in this area.
You can also use do-symbols or do-external-symbols if you prefer:
example:
>> (do-external-symbols (s (find-package :foo-package)) (print s))
FOO-PACKAGE:XXX
FOO-PACKAGE:YYY
FOO-PACKAGE:ZZZ
NIL
Where XXX, YYY & ZZZ are all external symbols in the package :foo-package.