Let, flet, macrolet : is there a way to do a "class-let"? - common-lisp

I have a macro which defines a class under certain rules, pseudo-code :
(defvar *all-my-classes* nil)
(defmacro my-macro (param)
`(if ,param
(progn
(defclass class-A () ...)
(push class-A *all-my-classes*))
(progn
(defclass class-B () ...)
(push class-B *all-my-classes*))))
I want to test the behaviour of the macro. Let is a convenient tool to mock variables. If I have an instance of *all-my-classes* running, I just have to do :
(let ((*all-my-classes* my-new-value)) ; generally `nil` for the test
(my-macro false))
But I would like to conserve the correspondance between *all-my-classes* and the classes defined. Since I want to test all the cases, let us suppose class-A is defined in the current environment, and i want to test if running (my-macro false) correctly defines class-B.
Since it is just a test, I would like the test to assert that class-B is currently defined, and that class-A is undefined in the current local environment; then when the test is over, class-B is undefined in the global environment, and class-A is still defined (without any alteration).
This way would be the best for my use :
(let ((*all-my-classes* nil))
(class-let ((class-A nil) ; or a way to map to a pre-defined
(class-B nil)) ; empty class temporarily.
(my-macro false)
(and
;; assert that the class is added to the list
(eql (length *all-my-classes*) 1)
;; assert that class-A is not defined
(null (find-class 'class-A))
;; assert that class-B is defined
(find-class 'class-B))))
I've searched to see if I can undefine a class, but it seems to be complex and implementation-dependent. And I want to preserve the current environment.
Restarting LISP each time for each tests is too long, and I would prefer a solution without having to load-unload packages for each tests (I don't know if it could work and if the classes will be garbage-collected when unloading the package...).
Thank you for your answers.

I do not think so.
The mechanism of how classes are stored is completely implementation defined, they just need to conform to the MOP (at least as far as it is mandated by the standard). However, the MOP does not prescribe anything that would make the classes registry dynamic. In fact, types and class names are specified to be part of the global environment (CLHS ch. 3.1.1.1), so it would be difficult for a conforming implementation to get dynamic here.
As you wrote, there is also no specified way to get rid of a class once defined.
As a rationale, I think that without this it would be very difficult to provide the kind of optimized runtime that the existing implementations have. Class lookup needs to be fast.
Now, to get to the meta question: what are you trying to do? Usually, while code is data, you should not confuse program logic with the programmed logic. What you propose looks like it might be intended to have code represent data. I'd advise to think about a clean separation and orthogonal representation.

Related

Franz LISP to Common LISP conversion questions

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?

common lisp's let binds, not executes?

In this code
(defun foo ()
. . .
(let ((bar (foo)))
(if bar
. . .)))
in the let line, let is only binding, right? It doesn't actually run foo. I assume foo is run (recursively) for the first time in the if statement, correct? If what I assume is correct, is there a way to have let actually execute foo and then assign the results to bar?
There's an answer that shows an example that illustrates the behavior of let. However, an example via an implementation doesn't answer conclusively whether it's supposed to behave that way, or whether implementations are free to do different things, or whether there's a bug in the implementation. To know what's supposed to happen, you need to check the documentation. Fortunately, the Common Lisp HyperSpec is freely available online. The documentation for let says:
Special Operator LET, LET*
let and let* create new variable bindings and execute a series of
forms that use these bindings. let performs the bindings in parallel
and let* does them sequentially.
The form
(let ((var1 init-form-1)
(var2 init-form-2)
...
(varm init-form-m))
declaration1
declaration2
...
declarationp
form1
form2
...
formn)
first evaluates the expressions init-form-1, init-form-2, and so on,
in that order, saving the resulting values. Then all of the variables
varj are bound to the corresponding values; each binding is lexical
unless there is a special declaration to the contrary. The expressions
formk are then evaluated in order; the values of all but the last are
discarded (that is, the body of a let is an implicit progn).
Thus, all the forms are evaluated (executed), then the results are bound the values, and then the forms in the body are evaluated.
In the example you provided, foo is evaluated and then assigned to bar. You can test this by simply evaluating something like:
(let ((foo (+ 1 2)))
(if (= foo 3)
foo
nil))
; => 3
cf. PCL: Syntax and Semantics or Lispdoc.
Edit
As #paulo-madeira brought up in the comments, this is not enough to test, since you don't know when each one was evaluated. See his comment for a way to test it using FORMAT. Anyway, the takeaway is, the LET you propose evaluates foo and assigns it to bar, which means your function foo is defined in terms of itself, which means you're up to no good.

What is the purpose of &environment in Common Lisp?

I am confused about the &environment parameter in common lisp. In particular, what is it useful for, and why is it a parameter, rather than a special variable?
EDIT: It would also be nice to see a concrete example of how &environment would be used in code.
Doc
Macro Lambda Lists:
&environment is followed by a single variable that is bound to an
environment representing the lexical environment in which the macro
call is to be interpreted. This environment should be used with
macro-function, get-setf-expansion, compiler-macro-function, and
macroexpand (for example) in computing the expansion of the macro, to
ensure that any lexical bindings or definitions established in the
compilation environment are taken into account.
Explanation
Operators which define macros (local or global) have to define how code is expanded before being evaluation or compiled, and thus may need to expand existing macros, whose expansion may depend on the environment - so the macro definitions need the environment.
Not a Special Variable
Special variables are more dangerous because the user might rebind them and because they are harder to handle correctly in multi-threaded code.
Example
All over the places.lisp:
(defmacro psetf (&whole whole-form
&rest args &environment env)
(labels ((recurse (args)
(multiple-value-bind (temps subforms stores setterform getterform)
(get-setf-expansion (car args) env)
(declare (ignore getterform))
(when (atom (cdr args))
(error-of-type 'source-program-error
:form whole-form
:detail whole-form
(TEXT "~S called with an odd number of arguments: ~S")
'psetf whole-form))
(wrap-let* (mapcar #'list temps subforms)
`(MULTIPLE-VALUE-BIND ,stores ,(second args)
,#(when (cddr args) (list (recurse (cddr args))))
,#(devalue-form setterform))))))
(when args `(,#(recurse args) NIL))))
From iolib/src/new-cl/definitions.lisp:
(defmacro defconstant (name value &optional documentation
&environment env)
(destructuring-bind (name &key (test ''eql))
(alexandria:ensure-list name)
(macroexpand-1
`(alexandria:define-constant ,name ,value
:test ,test
,#(when documentation `(:documentation ,documentation)))
env)))
For example when Lisp wants to macroexpand a form, it needs to know which name refers to what macro definition. This can depend on the globally available macros, locally bound macros via macrolet or what is available during compilation. Thus the environment object makes it possible to macroexpand a form with respect to different environments.
Environment variables provide to the macro author information about the declarations et. al. enforce when the macro was invoked. The macro author can then use that information to customize how he chooses to expand the macro. For example he might add or subtract debugging code based on declarations he finds in the environment. For example he might infer the type of expressions he was given to work with and add or subtract code to get better performance.
You can write a lot of Common Lisp code and never use &environment. There are situations where it becomes necessary, when those are would make good follow up question. Rainer hints at an answer to that.

Unbound variable in Common Lisp

I'm new to Lisp and I was reading about an text-generator example in ANSI Common Lisp, Chapter 8. I follow the example and defined a function "see" in the scope of a LET variable "prec",
(let ((prec '|.|))
(defun see (symb)
(let ((pair (assoc symb (gethash prev *words*))))
(if (null pair)
(push (cons symb 1) (gethash prev *words*))
(incf (cdr pair))))
(setf prev symb)))
and saved it into a lisp file.
Then when I returned to REPL and tried to invoke see after loading the compiled version of the file, an error occurred:
The variable PREV is unbound.
[Condition of type UNBOUND-VARIABLE]
How do I invoke see properly? And what's a lexical closure for? I'm all confused.
Thanks for helping.
Looks like you've typed prec instead of prev in the enclosing let form.
Lexical closures are functions that 'close over' a part of the lexical environment (hence the name). There are many good introductions to closures in lisp that I will not attempt to repeat here but, essentially, let is the most common way to manipulate the lexical environment; in this case, you want to add the binding for prev, which will then be available to code within the body of the form. Your function see will 'close over' this binding, and so each call to see has access to it, even though when you make these calls, you will no longer be in the lexical environment established by the let form. You could say the function definition takes the binding with it, in a sense.
As you appear to have mis-typed the name of the prev, your function is trying to refer to a binding that has not been established at that point in the code.

Optional Arguments in defgeneric?

I'm writing some methods to emit HTML for various elements. Each method has the same output, but doesn't necessarily need the same input.
The method for echoing a game-board needs to take a player as well (because each player only sees their own pieces)
(defmethod echo ((board game-board) (p player)) ... )
Echoing a board space doesn't require changing per player (that dispatch is actually done in the game-board method, which later calls echo on a space). Ideally, I'd be able to do
(defmethod echo ((space board-space)) ... )
(defmethod echo ((space empty-space)) ... )
It's also conceivable that I later run into an object that will need to know more than just the player in order to display itself properly. Since there are already methods specializing on the same generic, though, that would give the error
The generic function #<STANDARD-GENERIC-FUNCTION ECHO (4)> takes 2 required arguments;
It seems less than ideal to go back and name these methods echo-space, echo-board and so on.
Is there a canonical way of varying other arguments based on the specialized object? Should I do something like
(defgeneric echo (thing &key player ...) ...)
or
(defgeneric echo (thing &rest other-args) ...)
? More generally, can anyone point me to a decent tutorial on defgeneric specifically? (I've read the relevant PCL chapters and some CLOS tutorials, but they don't cover the situation I'm asking about here).
Generally speaking if interfaces of two functions are too different it indicates that they are not actually a specializations of the same operation and should not have the same name. If you only want to specialize on optional/key arguments the way to achieve that is to use a normal function which calls a generic function and provides it with default values for missing arguments to specialize on.
Keene's book is, I believe, the most comprehensive guide to CLOS. Unfortunately it seems to be available only in book form.
It's easier whenever the methods take the same parameters but only differ on the data type. But in cases where the generic function and methods all use the same name (of course) yet have lambda lists that vary significantly, I personally tend to use &key parameters to make my intentions explicit, rather than using &optional parameters. I think it helps with readability later.
Try something like this (pretending we already have classes class-a and class-b):
(defgeneric foo (object &key &allow-other-keys)
(:documentation "The generic function. Here is where the docstring is defined."))
(defmethod foo ((object class-a) &key &allow-other-keys)
(print "Code goes here. We dispatched based on type Class A."))
(defmethod foo ((object class-b) &key (x 1) (y 2) &allow-other-keys)
(print "Code goes here. We dispatched based on type Class B. We have args X and Y."))
Since "method combination" is involved, in order for the dispatching logic to "flow" through its possible choices of methods to use, we need to think of the method definitions somewhat like a chain where incompatible lambda lists (i.e. parameter lists) will break that chain. That's why there's the &key and &allow-other-keys in the method definitions that don't specifically need them. Putting them in DEFGENERIC and the method definitions allows us to have the method definition where we dispatch based on class-b.
DISCLAIMER: I'm a Common Lisp newbie myself, so take this with a grain of salt!
What about restructuring the objects/classes so that each object you want to echo has all required properties in its own (and inherited) slots?
That way you don't have to pass them as arguments when you call echo on the object, since what you need is already stored in the object.
Stepped in the same problem today and found a nice workaround, but it will work only if you have a fixed number of class-specified args, placed at the beginning.
The example for the single class-specific arg:
(defgeneric foo (obj &rest args))
(defmacro my-defmethod (name args code)
(let ((rst (gensym))
`(defmethod ,name (,(car args) &rest ,rst)
(apply (lambda (,(caar args) ,(cdr args)) ,code) (cons ,(caar args) ,rst)))))
(my-defmethod foo ((x class1) y z &optional a b c)
...)
(my-defmethod foo ((x class2) &key a b)
...)

Resources