How to execute a function on setf place - common-lisp

I have a list which contains some symbols and values. The goal is to setf the class slot with the accessor, whose symbol is provided by the list :
(defclass my-class ()
((attr :accessor attr)))
(let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(setf `(,(car to-call) obj) (cadr to-call)))
I have tried via a macro :
(defmacro call-accessor (to-call)
`(setf (,(car to-call) obj) "some-value"))
(let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(call-accessor to-call))
Which fails too, since to-call is a symbol and not a list.
eval does not work since to-call is a lexical variable;
It is not possible to do a let over the macro to give it the list;
I have tried with with-slots and with-accessors but the problem remains the same, because they are macros too.
I have considered macros which declares other macro, and symbol-macrolet too.
How can I setf a slot via an accessor corresponding to a symbol in my list ?
Thank you.

Calling the accessor function
The goal is to setf the class slot with the accessor
An accessor is a pair of functions. You can get the part which sets the value via FDEFINITION. The name of the function is a list (SETF accessor-name ). This is unusual: Common Lisp has in this case function names which are not symbols, but lists.
CL-USER 14 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(funcall (fdefinition `(setf ,(first to-call)))
(second to-call)
obj)
(describe obj))
#<MY-CLASS 40200614FB> is a MY-CLASS
ATTR "some-value"
Using a function call-accessor:
CL-USER 25 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(flet ((call-accessor (obj to-call)
(funcall (fdefinition `(setf ,(first to-call)))
(second to-call)
obj)))
(call-accessor obj to-call)
(describe obj)))
#<MY-CLASS 402000220B> is a MY-CLASS
ATTR "some-value"
using SETF with APPLY to hide the FDEFINITION call
To use setf with a computed accessor, one might need to use an apply form and a custom function.
Something like call-accessor would naturally be a function, because it does runtime lookup and takes values. Trying to use a macro would be more useful if the accessor would be known at compile time.
CL-USER 23 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(flet (((setf my-setter) (new-value object accessor)
(funcall (fdefinition `(setf ,accessor))
new-value
obj)))
(flet ((call-accessor (obj to-call)
(setf (apply #'my-setter obj (list (first to-call)))
(second to-call))))
(call-accessor obj to-call)
(describe obj))))
#<MY-CLASS 40200009AB> is a MY-CLASS
ATTR "some-value"
Choice of data structure
I think it's okay to compute accessor functions and similar. There may be use cases for that. CLOS was designed to be dynamic and reflective to allow these things.

You can use setf of slot-value, using the slot name symbol in your data pair:
(let ((to-call '(attr "some-value")))
(setf (slot-value obj (first to-call)) (second to-call)))
However, using slot-value directly is usually only sensible when you are wrangling at object internals (like initialize-instance methods/combinations; or maybe you are working on some serialization mechanism).
If that is not the case, you are using the object just as an associative data structure. I'd suggest using an alist, plist, or hash-map instead.

I agree that an alternative structure like a hash-table should probably be used. But to extend Svante's answer regarding slot-value, the working code is simply:
(setf (slot-value obj 'attr) "some-value")

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.

Accessing an encapsulated class slots within the encapsulating class method in common lisp

I have the following basic classes and methods:
(defgeneric connect-edge (edge))
(defclass Node ()
((forward-edges :initform nil)
(backward-edges :initform nil)
(value :initform 0.0)))
(defclass Edge ()
((value :initform 0.0)
(nodes :initform nil)))
(defmethod connect-edge ((edge Edge))
;; does nothing important. Simplified to cause the problem
(slot-value (car (slot-value edge 'nodes)) 'forward-edges))
I simplified the method enough to give me an error. Basically it doesn't do anything useful at this point, but it is enough to demonstrate the problem.
Setup:
The Edge class has nodes which is a list of Node objects. The Node class has lists of Edge objects.
Intention:
Read/ write the forward-edges and backward-edges in the Node objects encapsulated within an Edge object (the node list)
Problem/Question:
This "works" by returning nil as expected:
(defparameter *edge* (make-instance 'Edge))
(setf (slot-value *edge* 'nodes) (list (make-instance 'Node) (make-instance 'Node)))
(connect-edge *edge*)
This code gives me the error below, Why?
(connect-edge (make-instance 'Edge))
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION (SB-PCL::SLOT-ACCESSOR :GLOBAL
COMMON-LISP-USER::FORWARD-EDGES
SB-PCL::READER) (1)>
when called with arguments
(NIL).
Also, if I do this, I get the error below, which I think I understand why: No generic function defined which takes nil:
(connect-edge nil)
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::CONNECT-EDGE (1)>
when called with arguments
(NIL).
[Condition of type SIMPLE-ERROR]
Why am I doing all this?
I have the following code which causes (perhaps for different reason) similar error:
(defun make-classic (net)
(loop
for this-layer in net
for next-layer in (cdr net)
do
(loop
for this-node in this-layer
do
(loop
for next-node in next-layer
do
(let ((edge (make-instance 'Edge)))
(setf (slot-value edge 'nodes) '(this-node next-node))
(format t "Type of edge is ~a~%" (type-of edge))
;; Error is here
(connect-edge edge))))))
I wasn't sure if the error is due to passing a scoped variable, so I ended up trying to pass a (make-instance 'Edge) to cause the error.
This is all you need:
when called with arguments (NIL).
(slot-value (make-instance 'Edge) 'nodes)
is nil, so
(slot-value (car (slot-value edge 'nodes)) 'forward-edges))
fails.

Using dot notation to access CLOS slots

When accessing class slots, instead of writing
(defmethod get-name ((somebody person) (slot-value somebody 'name))
is it possible to use the dot notation aka C++, namely
(defmethod get-name ((somebody person) somebody.name) ?
Otherwise, when there are many slot operations in a method, (slot-value... creates a lot of boilerplate code.
I have figured out the answer today and I am just posting it as a Q&A, but if there are better solutions or there are problems I should expect with my solution, feel free to add new answers or comments.
The library access provides a dot notation reader macro for accessing slots (and hash-tables and other things). After enabling the reader macro by calling (access:enable-dot-syntax) you'll able to use #D. to access a slot name with the dot syntax popular in other languages.
(defclass person ()
((name :initarg :name :reader name)))
CL-USER> (access:enable-dot-syntax)
; No values
CL-USER> (defvar *foo* (make-instance 'person :name "John Smith"))
*FOO*
CL-USER> #D*foo*
#<PERSON #x302001F1E5CD>
CL-USER> #D*foo*.name
"John Smith"
There is also a with-dot macro if you don't want to use a reader macro
CL-USER> (access:with-dot () *foo*.name)
"John Smith"
You should not write accessors by hand, nor use slot-value (outside of object lifecycle functions, where the accessors may not have been created yet). Use the class slot options instead:
(defclass foo ()
((name :reader foo-name
:initarg :name)
(bar :accessor foo-bar
:initarg :bar)))
Now you can use the named accessors:
(defun example (some-foo new-bar)
(let ((n (foo-name some-foo))
(old-bar (foo-bar some-foo)))
(setf (foo-bar some-foo) new-bar)
(values n old-bar)))
Often, you want your classes to be "immutable", you'd use :reader instead of :accessor then, which only creates the reader, not the setf expansion.
The easiest solutions seems to be a reader macro that overloads . so that (slot-value somebody 'name) can be written as .somebody.name My strategy is to read somebody.name as a string (we need to define a non-terminating macro character so that the reader does not stop mid-string), and then process the string to construct the appropriate (slot-value...
I will need two helper functions:
(defun get-symbol (str)
"Make an uppercase symbol"
(intern (string-upcase str)))
(defun split-string (str sep &optional (start 0))
"Split a string into lists given a character separator"
(let ((end (position sep str :start start)))
(cons (subseq str start end) (if end (split-string str sep (1+ end))))))
And then I can define my reader macro:
(defun dot-reader (stream char)
(declare (ignore char))
(labels ((make-query (list)
(let ((car (car list))
(cdr (cdr list)))
(if cdr `(slot-value ,(make-query cdr) (quote ,(get-symbol car)))
(get-symbol car)))))
(make-query (nreverse (split-string (symbol-name (read stream)) #\.)))))
Finally, I need to register this reader macro:
(set-macro-character #\. #'dot-reader t)
Now it is possible to write:
(defmethod get-name ((somebody person) .somebody.name)
or, if name is itself a class,
(defmethod get-name ((somebody person) .somebody.name.first-name)
One restriction is that s-expressions will not work between the dots, say
.(get-my-class).name
won't work.

Is there a way to access slots in the superclass list in CLOS?

Is there a way to access slots of superclasses in CLOS?
E.g., in Objective C I can perform
- (void) frob {
[super frob]
}
This sends a message to the (sole) superclass of frob.
Perusing the CLOS documentation suggests that DEFCLASS merges all superclass information on class creation and thus this ability to communicate with the superclass is lost. Is this correct?
edit:
The scenario is somewhat unusual:
Given classes
(defclass animal ()
((behavior-types
:initform '(:eat :sleep :drink)
:reader behavior-types)))
(defclass cow (animal)
((behavior-types
:initform '(:moo :make-milk)
:reader behavior-types))
(defclass horse
((behavior-types
:initform '(:buck :gambol :neigh)
:reader behavior-types))
How to have a method, say, BEHAVIOR-TYPES or GET-BEHAVIOR that, when called with an object of type horse, returns '(:eat :sleep :drink :buck :gambol :neigh). That is to say, inheritance via a slot "adds" to the initform rather than replaces it.
An easy solution is, rather than to assign the data to the class, to have a generic method like so:
(defgeneric behavior-types (obj))
(defmethod behavior-types ((obj animal)) nil)
(defmethod behavior-types :around ((obj animal))
(append '(:eat :sleep :drink)
(call-next-method obj)))
(defmethod behavior-types :around ((obj horse))
(append '(:gambol :neigh :buck)
(call-next-method obj)))
However, this solution moves the data into the defgeneric rather than the class, where it properly belongs. So the motivation for the question came forth out of this.
At any rate - the question as asked reflected a misunderstanding of CLOS's design. It is not possible, as asked and within the normal framework, to perform this task. However, two separate approaches are given below using the MOP to solve the problem I posed.
The title of your question makes it sound like you're asking about how to access slots, but the code you show seems more like it's about calling methods that have been specialized for the superclass. If it's the latter that you're looking for, you should take a look at call-next-method, as well as 7.6 Generic Functions and Methods from the HyperSpec.
Calling “superclass methods”
In CLOS, methods don't belong to classes like they do in some other languages. Instead, there are generic functions on which specialized methods are defined. For a given argument list, a number of methods may be applicable, but only one is most specific. You can call the next most specific method with call-next-method. In the following transcript, there's a class FOO and a subclass BAR, and a generic function FROB which has methods specialized for FOO and BAR. In the method specialized for BAR, there's a call to call-next-method which, in this case, calls the method specialized for FOO.
CL-USER> (defclass foo () ())
;=> #<STANDARD-CLASS FOO>
CL-USER> (defclass bar (foo) ())
;=> #<STANDARD-CLASS BAR>
CL-USER> (defgeneric frob (thing))
;=> #<STANDARD-GENERIC-FUNCTION FROB (0)>
CL-USER> (defmethod frob ((foo foo))
(print 'frobbing-a-foo))
;=> #<STANDARD-METHOD FROB (FOO) {1002DA1E11}>
CL-USER> (defmethod frob ((bar bar))
(call-next-method)
(print 'frobbing-a-bar))
;=> #<STANDARD-METHOD FROB (BAR) {1002AA9C91}>
CL-USER> (frob (make-instance 'bar))
FROBBING-A-FOO
FROBBING-A-BAR
;=> FROBBING-A-BAR
Simulating it with method combinations
You can use method combinations to combine the results of the methods that are applicable to a list of arguments. For instance, you can define a method a with the method combination list that means when you call (a thing), all the methods on a applicable for the argument are called, and their results are combined into a list. If you give your slots in the different classes different names, and specialize methods on a that read those values, you can simulate the sort of thing you're looking for. This doens't prevent you from also using a traditional reader that accesses the slot, as well (e.g., get-a in the following example). The following code shows an example:
(defgeneric a (thing)
(:method-combination list))
(defclass animal ()
((animal-a :initform 'a :reader get-a)))
(defmethod a list ((thing animal))
(slot-value thing 'animal-a))
(defclass dog (animal)
((dog-a :initform 'b :reader get-a)))
(defmethod a list ((thing dog))
(slot-value thing 'dog-a))
(a (make-instance 'dog))
(get-a (make-instance 'animal))
;=> A
(get-a (make-instance 'dog))
;=> B
Using the MOP
This post from 1998 on Allegro CL archives is worth a read. It sounds like the author is looking for something similar to what you're looking for.
I need to define an inheritance behavior that concatenates
string-values of superclass-initforms with local slot initforms. E.g.
(defclass super()
((f :accessor f :initform "head")) (:metaclass user-class))
(defclass sub(super)
((f :accessor f :initform "tail")) (:metaclass user-class))
I'd like to get the following:
(f(make-instance'sub)) -> "head tail"
I didn't find a standard option in defclass slot-descriptions for
this. I'd like to define the concatenate combination for each
meta-class 'user-class'.
The response (by Heiko Kirschke, not me, but also see this response from Jon White with a similar approach), defines a new type of class:
(defclass user-class (standard-class) ())
and specializes clos:compute-effective-slot-definition to provide an initform that's computed from the slot definitions of the class and its superclass(es):
(defmethod clos:compute-effective-slot-definition
((the-class user-class) slot-name
;; The order of the direct slots in direct-slot-definitions may
;; be reversed in other LISPs (this is code written & tested with
;; ACL 4.3):
direct-slot-definitions)
(let ((slot-definition (call-next-method))
(new-initform nil))
(loop for slot in direct-slot-definitions
as initform = (clos:slot-definition-initform slot)
when (stringp initform)
do
;; Collecting the result string could be done perhaps more
;; elegant:
(setf new-initform (if new-initform
(concatenate 'string initform " "
new-initform)
initform)))
(when new-initform
;; Since at (call-next-method) both the initform and
;; initfunction of the effective-slot had been set, both must be
;; changed here, too:
(setf (slot-value slot-definition 'clos::initform) new-initform)
(setf (slot-value slot-definition 'clos::initfunction)
(constantly new-initform)))
slot-definition))
Then it's used like this:
(defclass super ()
((f :accessor f :initform "head"))
(:metaclass user-class))
(defclass sub(super)
((f :accessor f :initform "tail"))
(:metaclass user-class))
(f (make-instance 'sub))
==> "head tail"
This is getting into MOP functionality that's not specified by the spec, so you might have to adapt it for your particular implementation. There are some MOP compatibility layer packages out there that might be able to help you out, though.
There is no such concept as the instance slot of a superclass in CLOS.
If you create an instance, it has all slots. All slots from the class and its superclasses.
If a class has a slot FOO and some superclasses have also slots named FOO, all those are merged into one slot. Each instance of that CLOS class will have that slot.
Still you need to be more careful with your wording. Superclasses are objects themselves and they have slots themselves. But this has nothing to do with an instance having local slots and having superclasses with instance slots. The latter does not exist in CLOS.
CL-USER 18 > (defclass bar () (a b))
#<STANDARD-CLASS BAR 413039BD0B>
Above is then a superclass with two slots.
CL-USER 19 > (defclass foo (bar) (b c))
#<STANDARD-CLASS FOO 4130387C93>
Above is a class with two local and one inherited slot. The slot b is actually merged from this class and from the superclass.
CL-USER 20 > (describe (make-instance 'foo))
#<FOO 402000951B> is a FOO
B #<unbound slot>
C #<unbound slot>
A #<unbound slot>
Above shows that the instance has three slots and all can be directly accessed. Even the slot `a, which was defined in the superclass.
If we look at the actual superclass as an instance itself, we see its slots:
CL-USER 21 > (describe (find-class 'bar))
#<STANDARD-CLASS BAR 413039BD0B> is a STANDARD-CLASS
NAME BAR
DEFAULT-INITARGS NIL
DIRECT-DEFAULT-INITARGS NIL
DIRECT-SLOTS (#<STANDARD-DIRECT-SLOT-DEFINITION A 4020005A23> #<STANDARD-DIRECT-SLOT-DEFINITION B 4020005A93>)
DIRECT-SUBCLASSES (#<STANDARD-CLASS FOO 4130387C93>)
DIRECT-SUPERCLASSES (#<STANDARD-CLASS STANDARD-OBJECT 40F017732B>)
PRECEDENCE-LIST (#<STANDARD-CLASS BAR 413039BD0B> #<STANDARD-CLASS STANDARD-OBJECT 40F017732B> #<BUILT-IN-CLASS T 40F00394DB>)
PROTOTYPE NIL
DIRECT-METHODS NIL
WRAPPER #(1539 (A B) NIL #<STANDARD-CLASS BAR 413039BD0B> (#<STANDARD-EFFECTIVE-SLOT-DEFINITION A 4020005AFB> #<STANDARD-EFFECTIVE-SLOT-DEFINITION B 4020005B63>) 2)
LOCK #<MP::SHARING-LOCK "Lock for (STANDARD-CLASS BAR)" Unlocked 41303AD4E3>
DOCUMENTATION-SLOT NIL
PLIST (CLOS::COPYABLE-INSTANCE #<BAR 402000638B>)
POTENTIAL-INITARGS 0
MAKE-INSTANCE-FLAGS 509
OTHER-LOCK #<MP:LOCK "Lock for (OTHER STANDARD-CLASS BAR)" Unlocked 41303AD553>
REINITIALIZE-INITARGS 0
REDEFINE-INITARGS 0
DEPENDENTS NIL
This is really, really hasckish. I hope someone will step in and fix it, though it should illustrate the idea:
(defclass agent () ((behaviour :initform do-nothing :accessor behaviour-of)))
(defclass walk-agent (agent) ((behaviour :initform and-walk)))
(defclass talk-agent (walk-agent) ((behaviour :initform and-talk)))
(defmethod sb-mop:compute-effective-slot-definition
:after (class (name (eql 'behaviour)) sdlotds)
(setf *slot-def*
(loop
:for slot :in sdlotds :do
(format t "~&slot: ~s" (sb-mop:slot-definition-initform slot))
:collect (sb-mop:slot-definition-initform slot))))
(defmethod initialize-instance :before ((instance agent) &rest keyargs)
(declare (ignore keyargs))
(let (*slot-def*)
(declare (special *slot-def*))
(sb-mop:compute-slots (class-of instance))
(setf (behaviour-of instance) *slot-def*)))
;; (behaviour-of (make-instance 'talk-agent))
;; slot: AND-TALK
;; slot: AND-WALK
;; slot: DO-NOTHING
;; slot: AND-TALK
;; slot: AND-WALK
;; slot: DO-NOTHING
;; (AND-TALK AND-WALK DO-NOTHING)
PS. I see that the function that computes the list of slots definitions in SBCL is in std-class.lisp, std-compute-slots. So it isn't something that MOP defines in some way... But this one would be really helpful here.

Common lisp: Redefine an existing function within a scope?

In Common Lisp, is it possible to redefine an already defined function within a certain scope? For example, given a function A that calls a function B. Can I temporarily redefine B during a call to A?
I'm looking for something along the lines of a let block, but that can redefine functions.
Within a given lexical scope, yes. Use FLET or LABELS. Any function defined with FLET will be unable to call functions defined in the same lexical scope, if you want that (for, say, self-recursive of a group of mutually recursive functions), you will need to use LABELS.
Note that both FLET and LABELS only establish lexical shadowing, should not be used to shadow functions from the COMMON-LISP package and will not dynamically change what function is called from outside the lexical scope the form establishes.
Local functions can be introduced with FLET and LABELS.
If you want to redefine/shadow an existing function using dynamic scope, this is a macro I've been using for a while.
(defmacro! with-shadow ((fname fun) &body body)
"Shadow the function named fname with fun
Any call to fname within body will use fun, instead of the default function for fname.
This macro is intentionally unhygienic:
fun-orig is the anaphor, and can be used in body to access the shadowed function"
`(let ((fun-orig))
(cond ((fboundp ',fname)
(setf fun-orig (symbol-function ',fname))
(setf (symbol-function ',fname) ,fun)
(unwind-protect (progn ,#body)
(setf (symbol-function ',fname) fun-orig)))
(t
(setf (symbol-function ',fname) ,fun)
(unwind-protect (progn ,#body)
(fmakunbound ',fname))))))
Usage:
Clozure Common Lisp Version 1.9-r15759 (DarwinX8664) Port: 4005 Pid: 4728
; SWANK 2012-03-06
CL-USER>
(defun print-using-another-fname (x)
(print x))
PRINT-USING-ANOTHER-FNAME
CL-USER>
(let ((*warn-if-redefine-kernel* nil))
(with-shadow (print (lambda (x)
(funcall fun-orig (+ x 5))))
(print-using-another-fname 10)))
15
15
CL-USER>
(print 10)
10
10
CL-USER>
Note that it relies on Doug Hoyte's defmacro! macro, available in Let Over Lambda.
Also as written, it's anaphoric (fun-orig is available within the body). If you want it completely hygienic, just change the fun-orig's to ,g!fun-orig's.
I most often redefine functions when writing unit tests. Mocking functions within the scope of a particular unit test is helpful, and sometimes that needs to be done with dynamic (not lexical) scope.
You can simulate dynamic-binding for funs like this:
(defmacro setvfun (symbol function)
`(progn
(setf ,symbol ,function)
(setf (symbol-function ',symbol) (lambda (&rest args) (apply (symbol-value ',symbol) args)))))
and then ,for example, with
(setvfun some-fun (lambda() (format t "initial-definition~%")))
(defun test-the-fun (&rest args) (apply #'some-fun args))
(defun test ()
(test-the-fun)
(flet ((some-fun () (format t "Lexically REDEFINED (if you see this, something is very wrong)~%")))
(test-the-fun))
(let ((some-fun (lambda (x) (format t "Dynamically REDEFINED with args: ~a~%" x))))
(declare (special some-fun))
(test-the-fun "Hello"))
(test-the-fun))
you get:
REPL> (test)
==>initial-definition
==>initial-definition
==>Dynamically REDEFINED with args: Hello
==>initial-definition

Resources