Backquote symbol list ending in period - common-lisp

I'm curious if there is a way to end a backquoted symbol list with a period following a comma inserted value.
Here is the sample code:
(defparameter *things* '(book pencil shoe))
(defun inspect-item (item things)
(if (member item things)
`(you pick up the ,item and yeet it out the window.)
`(only realize the truth... there is no ,item.)))
This will succeed (print (inspect-item 'book *things*)) and it produces the symbol list (YOU PICK UP THE BOOK AND YEET IT OUT THE WINDOW.). I assume in this case that the period is part of the symbol WINDOW. (as confirmed using the last function).
However, this will fail (print (inspect-item 'spoon *things*)) claiming that variable ITEM. has no value (Because it thinks the name is item.). Leaving a space between item and the period gives the error illegal end of dotted list which I assume is because it is assuming I'm using dotted list syntax.
Is there any way to get it to produce the symbol I want at the end (BOOK.)?

Possible requirements for a solution
You need to create a new symbol based on an old one:
the same name, with a . appended
probably in the same package
Period as a symbol
Note that you can write a symbol with the period as a name using an escaped symbol: |.| or \..
CL-USER 17 > (let ((item 'foobar))
`(only realize the truth... there is no ,item \.))
(ONLY REALIZE THE TRUTH... THERE IS NO FOOBAR \.)
princ prints without escape characters:
CL-USER 18 > (princ '(ONLY REALIZE THE TRUTH... THERE IS NO FOOBAR \.))
(ONLY REALIZE THE TRUTH... THERE IS NO FOOBAR .) ; <- printed output
(ONLY REALIZE THE TRUTH... THERE IS NO FOOBAR \.) ; <- REPL value
Solution
CL-USER 19 > (defun add-suffix (symbol suffix)
(intern (concatenate 'string
(symbol-name symbol)
(symbol-name suffix))
(symbol-package symbol)))
ADD-SUFFIX
CL-USER 20 > (let ((item 'tree))
`(we went to the ,(add-suffix item '|.|)))
(WE WENT TO THE TREE.)
It can also be useful to use the flexibility of format instead of using concatenate.
CL-USER 22 > (defun add-suffix (symbol suffix)
(intern (format nil "~a~a" symbol suffix)
(symbol-package symbol)))
ADD-SUFFIX

I think that the real problem lies in the attempt to produce text just as the printed representation of a list of symbols. Those are completely different things. The only reason to do it like this could be as some kind of exercise. There are a number of introductory texts that do that kind of thing.
Instead, I'd advise to actually produce text, i. e. strings. You might use the format function that is quite flexible for inserting variable things:
(defparameter *things* '("book" "pencil" "shoe"))
(defun inspect-item (item things)
(if (member item things :test #'string-equal)
(format nil
"You pick up the ~a and yeet it out the window."
item)
(format nil
"Only realize the truth… there is no ~a."
item)))

I partly agree with Svante that turning lists of symbols into text is often the wrong thing. On the other hand things like parsers &c often want to think in terms of symbols rather than strings as symbols have nice properties like (eq a b) working the way you would hope.
That being said, here's an alternative approach: create a list of words and then have a function which turns them into a string representing a sentence, adding appropriate punctuation and capitalization.
(defparameter *things* '(book pencil shoe))
(defun inspect-item (item things)
(sentencify (if (member item things)
`(you pick up the ,item and yeet it out the window)
`(only realize the truth... there is no ,item))))
(defun sentencify (slist &key (question nil))
(format nil "~{~A~^ ~}~A"
(loop for first = t then nil
for w in slist
for ws = (typecase w
(symbol (symbol-name w))
(string w)
(t w))
collect (typecase ws
(string (if first
(string-capitalize ws)
(string-downcase ws)))
(t ws)))
(if question "?" ".")))
And now:
> (inspect-item 'book *things*)
"You pick up the book and yeet it out the window."
> (inspect-item 'bok *things*)
"Only realize the truth... there is no bok."
> (inspect-item '(x . y) *things*)
"Only realize the truth... there is no (x . y).

Related

Controlling the printing of special cons forms (e.g printing (function +) as #'+ etc)

I want some reader macros to print as as shortened expression that the macro understands. Lets say I want to extend the #' macro to take #'~[rest-of-symbol] and turn that into (complement #'rest-of-symbol).
What controls how that is printed? On SBCL, for instance, '(function +) prints as #'+. How do i make '(complement #'listp) print as #~listp?
My first thought was
(defmethod print-object :around ((obj cons) stream)
;; if #'~fn-name / (complement (function fn-name))
;; => fn-name otherwise NIL
(let ((fn-name
(ignore-errors
(destructuring-bind (complement (function fn-name))
obj
(when (and (eq complement 'complement)
(eq function 'function))
fn-name)))))
(if fn-name
(format stream "#'~~~S" fn-name)
(call-next-method))))
This works insofar as (print-object '(complement #'evenp) *standard-output*) prints it the way I want, but the REPL doesn't. Also (print-object '#'+ *standard-output*) prints it as (function +) so the REPL isn't using print-object. With defining the print-object method for user defined classes the REPL always picks up on the new definition.
This is my first post and I'm sorry I can't get the code to format properly. If someone can put a link on how to do that I would appreciate it.
Evaluation time
You are mixing code with data in your example:
(function +)
Is a special form that evaluates to a function object, which admits a shorter syntax:
#'+
But when you are writing:
'(function +)
or
'(complement fn)
Then in both cases you are writing quoted, literal lists, which evaluates to themselves (namely a list starting with symbol function or complement, followed respectively by symbol + and fn).
However, you want the code to be evaluated at runtime to actual function objects; if you type this in the REPL:
(complement #'alpha-char-p)
The result is a value that is printed as follows:
#<FUNCTION (LAMBDA (&REST SB-IMPL::ARGUMENTS) :IN COMPLEMENT) {101AAC8D9B}>
You have an actual function object that you can funcall. In other words, by the time you reach print-object, you no longer have access to source code, you are manipulating data at runtime which happens to be functions. So you cannot use destructuring-bind to get the complement symbol that was present in the source code.
What you need to do instead is to attach metadata to your function. There is a way to do that in Common Lisp by defining a new type of function, thanks to the Meta-Object Protocol.
Funcallable objects
I'm relying on Closer-MOP for all the symbols prefixed with c2cl: below. I define a new class of functions, annotated-fn, which is a function with addditional data:
(defclass annotated-fn (c2cl:funcallable-standard-object)
((data :initform :data :initarg :data :reader annotated-fn-data))
(:metaclass c2cl:funcallable-standard-class))
Notice that this class is a funcallable-standard-object (like the usual functions), and its metaclass is funcallable-standard-class. Such an object has an additional implicit slot that is a function to call.
More precisely, you have to call c2cl:set-funcallable-instance-function to set a function associated with the object, and when later you use funcall or apply with the object, then the wrapped function is called instead. So you can transparently use this class of functions wherever you usually use a function. It just has additional slots (here data).
For example, here is how I instantiate it, with a function to wrap additional data:
(defun annotate-fn (function data)
(let ((object (make-instance 'annotated-fn :data data)))
(prog1 object
(c2cl:set-funcallable-instance-function object function))))
Let's try it:
(describe
(annotate-fn (constantly 3)
'(:category :constantly)))
#<ANNOTATED-FN {1006275C7B}>
[funcallable-instance]
Lambda-list: UNKNOWN
Derived type: FUNCTION
Documentation:
T
Source file: SYS:SRC;CODE;FUNUTILS.LISP
Slots with :INSTANCE allocation:
DATA = (:CATEGORY :CONSTANTLY)
You can also use this object like any other function.
Now, your reader macros can expand into calls to annotate-fn, and add any kind of additional metadata you need to the function.
Reader macro
For our example, imagine you define a reader macros for constant functions:
(set-macro-character #\[ 'read-constantly t)
(set-macro-character #\] (get-macro-character #\) nil))
(defun read-constantly (stream char)
(declare (ignore char))
(let* ((list (read-delimited-list #\] stream t))
(value (if (rest list) list (first list)))
(var (gensym)))
`(let ((,var ,value))
(annotate-fn (constantly ,var)
(list :category :constantly
:constant ,var)))))
Using this syntax:
> [(+ 8 5)]
=> #<ANNOTATED-FN ...>
By the way, the syntax I defined also allows the following:
> [+ 8 5]
Pretty-printing
Let's define a generic function that prints an annotated function given its :category field:
(defgeneric print-for-category (category data object stream))
(defmethod print-object ((o annotated-fn) s)
(let* ((data (annotated-fn-data o))
(category (getf data :category)))
(print-for-category category data o s)))
Then, we can specialize it for :constantly, and here we assume also that the data associated with the function contains a :constant field:
(defmethod print-for-category ((_ (eql :constantly)) data o s)
(format s "[~s]" (getf data :constant)))
For example:
(let ((value (+ 8 6)))
(annotate-fn (constantly value)
`(:constant ,value
:category :constantly)))
This above is printed as:
[14]
Which would be the same as your hypothetical reader macro.
To do this you need to understand the pretty printer. I have understood it in the past but no longer do completely. It dispatches on type and the trick for things like this is that you can specify very specific types for trees of conses, although doing so is verbose.
Here is an example which is almost certainly not completely correct, but does achieve what you want in this case:
(defparameter *ppd* (copy-pprint-dispatch))
(defun pprint-complement-function (s form)
;; This is the thing that the pretty printer will call. It can
;; assume that the form it wants to print is already correct.
(destructuring-bind (complement (function name)) form
(declare (ignore complement function))
(format s "#'~~~W" name)))
;;; Now set this in the table with a suitable hairy type specification
;;;
(set-pprint-dispatch '(cons (eql complement)
(cons (cons (eql function)
(cons t null))
null))
'pprint-complement-function
0
*ppd*)
And now
> (let ((*print-pprint-dispatch* *ppd*))
(pprint '(complement (function foo)))
(pprint '((complement (function foo)) (function foo))))
#'~foo
(#'~foo #'foo)
You can make the awful nested cons type specifier easier by defining this (which, perhaps, should be the compound type specifier for list except you can't do that):
(deftype list-of-types (&rest types)
(labels ((lot (tt)
(if (null tt)
'null
`(cons ,(first tt) ,(lot (rest tt))))))
(lot types)))
And then
(set-pprint-dispatch '(list-of-types (eql complement)
(list-of-types (eql function)
*))
'pprint-complement-function
0
*ppd*)
is perhaps easier to read.

using a struct as property list to macro

I have a struct with :name and :value that I'd like to use as arguments to a macro. But I'm not sure how to tell lisp that.
I can write out the call like
(sxql:yield (sxql:set= :name "a" :value 1))
"SET name = ?, value = ?"
("a" 1)
But I'd like to use an already existing structure
(defstruct my-struct name value)
(setq x (make-my-struct :name "a" :value 1))
; #S(MY-STRUCT :NAME "a" :VALUE 1)
using answers from Common LISP: convert (unknown) struct object to plist?
I've made
(defun struct-plist (x)
"make struct X into a property list. ugly kludge"
(let* ((slots (sb-mop:class-slots (class-of x)))
(names (mapcar 'sb-mop:slot-definition-name slots)))
(alexandria:flatten
(mapcar (lambda (n) (list (intern (string n) "KEYWORD")
(slot-value x n)))
names))))
(setq p (struct-plist x)) ; (:NAME "a" :VALUE 1)
My naive attempts are
(sxql:set= p) ; error in FORMAT: No more argument SET ~{~A = ~A~^, ~}
(funcall 'sxql:set= p) ; SXQL:SET= is a macro, not a function.
(macroexpand (sxql:set= p)) ; error in FORMAT ...
I imagine this is an easy/fundamental lisp programming question. But I'm not sure how to ask it (or search for answers). I'm also hoping there is an better struct<->plist story than what I've stumbled across so far.
EDIT: In case this is really an xy-problem. I've used flydata:defmodel to create the struct and I want to insert to a database using the same model.
This is definitely an xy problem: unfortunately I don't understand y (flydata?) well enough to answer the y part.
Here's why what you are trying to do can't work however. Consider this code in a file being compiled:
(defstruct mine name value)
...
(sxql:set= <anything derived from mine>)
Compiling this file must satisfy two constraints:
It does not fully create the structure type mine (see defstruct);
It must macroexpand sxql:set=.
What these constraints mean is that sxql:set= can't know about the structure at the time it is expanded. So any trick which relies on information about the structure must make that information available at compile time.
As I said, I don't understand the y part well enough to understand what you are trying to do, but a hacky approach to this is:
write a wrapper for defstruct which stashes information at compile time (strictly: at macro-expansion time);
write a wrapper for sxql:set= which uses that information to expand into something which makes sense.
Here is a mindless wrapper for defstruct. Note that this is mindless: it can only understand the most simple defstruct forms, and even then it may be wrong. It exists only as an example.
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *structure-information* '()))
(defmacro define-mindless-structure (name &body slots)
(assert (and (symbolp name)
(every #'symbolp slots))
(name slots)
"I am too mindless")
(let ((found (or (assoc name *structure-information*)
(car (push (list name) *structure-information*)))))
(setf (cdr found) (mapcar (lambda (slot)
(list slot (intern (symbol-name slot)
(find-package "KEYWORD"))
(intern (concatenate 'string
(symbol-name name)
"-"
(symbol-name slot)))))
slots)))
`(defstruct ,name ,#slots))
So now
(define-mindless-structure mine
name value)
Will expand into (defstruct mine name value) and, at macroexpansion time will stash some information about this structure in *structure-information*.
Now I stop really understanding what you need to do because I don't know what sxql:set= is meant to do, but it might be something like this:
(defmacro mindless-set= ((s o))
(let ((info (assoc s *structure-information*))
(ov (make-symbol "O")))
(unless info
(error "no information for ~A" s))
`(let ((,ov ,o))
(sxql:set= ,#(loop for (slot initarg accessor) in (cdr info)
;; the compiler will whine about slot annoyingly
collect initarg
collect `(,accessor ,ov))))))
So with this macro, assuming a suitable define-mindless-structure for mine form has been seen by the time the macro is expanded, then
(mindless-set= (mine it))
Will expand into
(let ((#:o it))
(set= :name (mine-name #:o) :value (mine-value #:o)))
But, as I said, I am not sure what the expansion you actually want is.
Finally, before even thinking about using anything like the above, it would be worth looking around to see if there are portability libraries which provide compile/macroexpansion-time functionality like this: there very well may be such, as I don't keep up with things.

Using a quoted list of slots for Peter Seibel's defclass macro

I am using the defclass macro from Practical Common Lisp, which takes as argument a list of symbols.
I would like to change the macro in order to let it accept a quoted list of symbols. This has the benefit of having thenm defined as constants that can be used in other convenience functions, e.g. here. I confused myself in trying to get this done.
My use case is following:
(defconstant state-slots '(unit motion mode moc-offset woc-pos woc-inc feed spindle))
;; would like to use the quoted list here:
(defclass-by-slots gc-state (unit ; :mm(=G21) :inch(=G20)
motor ; nil :on
motion ; :jog(=G0) :lin(=G1) :cw(=G2) :ccw(=G3)
mode ; :abs(=G90) :inc(=G91)
moc-offset ; woc-zero(xyz, mm) from moc-zero
woc-pos ; woc-pos(xyz, mm) from woc-zero
woc-inc
feed
spindle))
;; can use quoted slot list when using a convenience function, e.g:
(defun format-by-slots (o slots &optional str-type)
(let* ((f (lambda (s$) (eval (format-slot o s$))))
(str-type (string-upcase str-type))
(r (concatenate
'string
(format nil "~A (~A)" o (class-of o))
(reduce (lambda (s1 s2) (concatenate 'string s1 s2))
(loop for s in slots
when (funcall f s) collect it)
:from-end t :initial-value (format nil "~%")))))
(if str-type
(ppcre:regex-replace-all
(format nil "^#<~A \\{(\\d|[A-F])+\\}> " str-type)
r
(format nil "#<~A {...}> " str-type))
r)))
I am using this for several classes defined by different slots.
The nuisance is that I cannot have defined the slots uniformly for the type definition and the convenience functions which is source of annoying errors.
Solution based on [Rainer Joswig's answer] (https://stackoverflow.com/a/61154538/2336738):
(defmacro def-my-class (name supers slots-symbol)
"The value of a symbol of slots-symbol is used as the
list of slots."
`(defclass ,name ,supers
,(if (and (symbolp slots-symbol)
(symbol-value slots-symbol)
(listp (symbol-value slots-symbol)))
(mapcar #'slot->defclass-slot (symbol-value slots-symbol))
(error "~a is not a symbol which names a list of slot names" slots-symbol))))
Symbol values at compile time
If you want to use the value of a symbol at compile time, then you need to make sure that it is defined. Two usual ways to do that:
define the symbol in a file and load it before compiling another file where it is used
use EVAL-WHEN to execute a symbol definition (defvar, defparameter,defconstant` ...) at compile time
Read-time evaluation
One possibility to use a symbol value during compilation is to use read-time evaluation. You would need to make sure that the constant value of +state-slots+ is defined during compilation:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defconstant +state-slots+
'(unit motion mode moc-offset woc-pos woc-inc feed spindle)))
(defclass foo ()
#.+state-slots+)
Custom Macro
If the value of +state-slots+ is defined at compile time, then we can also use it in a macro:
(defmacro def-my-class (name supers slots-symbol)
"The value of a symbol of slots-symbol is used as the
list of slots."
`(defclass ,name ,supers
,(if (and (symbolp slots-symbol)
(symbol-value slots-symbol)
(listp (symbol-value slots-symbol)))
(symbol-value slots-symbol)
(error "~a is not a symbol which names a list of slot names"))))
(def-my-class foo () +state-slots+)

The format function looping over a dotted alist

I am working on a function that transforms an alist into a query parameters. So far it looks like this.
(defun encode-options (opts)
"Turns an alist into url query parameters."
(format nil "~{~{~A=~A~}~^&~}" opts))
This works perfectly for alists like ((a b) (c d)) (Resulting in "A=B&C=D"), but fails for dotted alists like ((a . b) (c . d)). (Resulting in The value B is not of type LIST.)
My question is: Is it possible to format the dotted alist to give me the expected results and how?
Is it possible to format the dotted alist?
No, format iterates over proper lists.
There are many possible ways to implement what you want. Here I present two of them.
Keep control string, change data
(defun ensure-proper-list (value)
(typecase value
(null nil)
(cons (cons (car value)
(ensure-proper-list (cdr value))))
(t (list value))))
Now, you transform the option argument so that all elements are proper lists:
(defun encode-options (options)
"Turns an alist into url query parameters."
(format nil
"~{~{~A=~A~}~^&~}"
(mapcar #'ensure-proper-list options)))
Keep data, change control string
(defun print-alist (stream data &optional colonp atsignp)
(declare (ignore colonp atsignp))
(destructuring-bind (head . tail) data
(format stream "~A=~A" head (if (consp tail) (first tail) tail))))
With this new format control, print the list as given:
(defun encode-options (options)
"Turns an alist into url query parameters."
(format nil
"~{~/lib:print-alist/~^&~}"
options))
Note that I added a package prefix lib because without a package, print-alist would be looked up in the user package (a.k.a. COMMON-LISP-USER), which in my opinion is rarely what you want. From 22.3.5.4 Tilde Slash: Call Function:
The function corresponding to a ~/name/ directive is obtained by
looking up the symbol that has the indicated name in the indicated
package. If name does not contain a ":" or "::", then the whole name
string is looked up in the COMMON-LISP-USER package.
That's why I would recommend to always mention the package with ~/ directives.

Can you get the "code as data" of a loaded function in Clojure?

To put it another, way, "Okay, so code is data..."
That thread addresses how to read from a source file, but I'm wondering how to get the s-expression of an already-loaded function into a data structure that I can read and manipulate.
In other words, if I say,
(defn example [a b] (+ a b))
can't I get that list at runtime? Isn't this the whole point of "code as data"?
This is really a general Lisp question, but I'm looking for an answer in Clojure.
You can use the clojure.repl/source macro to get the source of a symbol:
user> (source max)
(defn max
"Returns the greatest of the nums."
{:added "1.0"
:inline-arities >1?
:inline (nary-inline 'max)}
([x] x)
([x y] (. clojure.lang.Numbers (max x y)))
([x y & more]
(reduce1 max (max x y) more)))
nil
But this is only part of the answer. AFAICT source looks up the source filename and line number that define the given symbol, and then prints the source code from the file. Therefore, source will not work on symbols that you do not have the source for, i.e. AOT-compiled clojure code.
Coming back to your original question, you can think of source as reading the meta data associated with the given symbol and simply printing that. I.e. it's cheating. It's not in any way returning "code as data" to you, where with code I mean a compiled clojure function.
In my mind "code as data" refers to the feature of lisps where source code is effectively a lisp data structure, and therefore it can be read by the lisp reader. That is, I can create a data structure that is valid lisp code, and eval that.
For example:
user=> (eval '(+ 1 1))
2
Here '(+ 1 1) is a literal list which gets read by the clojure reader and then evaluated as clojure code.
Update: Yehonathan Sharvit was asking in one of the comments if it's possible to modify the code for a function. The following snippet reads in the source for a function, modifies the resulting data structure, and finally evaluates the data structure resulting in a new function, my-nth, being defined:
(eval
(let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
`(~(first src) my-nth ~#(nnext src))))
The syntax-quote line replaces nth with my-nth in the defn form.
You can get the source in recent versions of clojure with the source function.
user=> (source nth)
(defn nth
"Returns the value at the index. get returns nil if index out of
bounds, nth throws an exception unless not-found is supplied. nth
also works for strings, Java arrays, regex Matchers and Lists, and,
in O(n) time, for sequences."
{:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~#nf)))
:inline-arities #{2 3}
:added "1.0"}
([coll index] (. clojure.lang.RT (nth coll index)))
([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
nil
to get the string as a value you can wrap this in with-out-str:
user=> (with-out-str (source nth))
"(defn nth\n \"Returns the value at the index. get returns nil if index out of\n bounds, nth throws an exception unless not-found is supplied. nth\n also works for strings, Java arrays, regex Matchers and Lists, and,\n in O(n) time, for sequences.\"\n {:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~#nf)))\n :inline-arities #{2 3}\n :added \"1.0\"}\n ([coll index] (. clojure.lang.RT (nth coll index)))\n ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))\n"
user=>
That was my message; nice to meet you ;-) BTW, the references given in that thread for answers were excellent reading; so if you're interested, you might want to take the time to read them. Back to your question though source seems to work for code that was loaded through a file, but it doesn't work in all cases. I think, specifically, it doesn't work for functions defined in the repl.
user=> (def foo (fn [] (+ 2 2)))
#'user/foo
user=> (source foo)
Source not found
nil
user=> (defn foo2 [] (+ 2 2))
#'user/foo2
user=> (source foo2)
Source not found
nil
Digging a little bit...
user=> (source source)
(defmacro source
"Prints the source code for the given symbol, if it can find it.
This requires that the symbol resolve to a Var defined in a
namespace for which the .clj is in the classpath.
Example: (source filter)"
[n]
`(println (or (source-fn '~n) (str "Source not found"))))
nil
user=> (source clojure.repl/source-fn)
(defn source-fn
"Returns a string of the source code for the given symbol, if it can
find it. This requires that the symbol resolve to a Var defined in
a namespace for which the .clj is in the classpath. Returns nil if
it can't find the source. For most REPL usage, 'source' is more
convenient.
Example: (source-fn 'filter)"
[x]
(when-let [v (resolve x)]
(when-let [filepath (:file (meta v))]
(when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
(with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
(dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
(let [text (StringBuilder.)
pbr (proxy [PushbackReader] [rdr]
(read [] (let [i (proxy-super read)]
(.append text (char i))
i)))]
(read (PushbackReader. pbr))
(str text)))))))
nil
So yeah, it looks like it tries to load the source file off the classpath to try to spit it out for you. One thing I've learned when working with Clojure is that 9 times out of 10 it is useful to look at the source.

Resources