Calling Class allocated slot on class-names in Common Lisp - class

Is there any way to call a :class allocated slot on the name of a class instead of an instance? Something like: (class-alloc-slot 'name-of-the-class)

LispWorks:
CL-USER 6 > (defclass foo () ((bar :allocation :class :initform :baz)))
#<STANDARD-CLASS FOO 402005B3CB>
CL-USER 7 > (make-instance 'foo)
#<FOO 4020240C33>
CL-USER 8 > (class-prototype (find-class 'foo))
#<FOO 402005EB73>
CL-USER 9 > (slot-value * 'bar)
:BAZ
use CLOSER-MOP for portable MOP features.

Related

LISP macro that process variables and data structure inside at runtime

I have LISP written in JavaScript (https://jcubic.github.io/lips/ with online demo where you can try it) and I have macro like this:
(define-macro (globalize symbol)
(let ((obj (--> (. lips 'env) (get symbol))))
`(begin
,#(map (lambda (key)
(print (concat key " " (function? (. obj key))))
(if (function? (. obj key))
(let* ((fname (gensym))
(args (gensym))
(code `(define (,(string->symbol key) . ,args)
(apply (. ,obj ,key) ,args))))
(print code)
code)))
;; native Object.key function call on input object
(array->list (--> Object (keys obj)))))))
In this code I use this:
(let ((obj (--> (. lips 'env) (get symbol))))
and I call this macro using:
(globalize pfs)
to create function for each static method of pfs (which is LightingFS from isomorphic-git where each function return a promise, it's like fs from node).
But it will not work for something like this:
(let ((x pfs))
(globalize x))
because lips.env is global enviroment.
So my question is this how macro should work? Should they only process input data as symbols so they never have access to object before evaluation of lisp code?
How the LISP macro that generate bunch of functions based on variable should look like. For instance in scheme if I have alist in variable and want to generate function for each key that will return a value:
input:
(define input `((foo . 10) (bar . 20)))
output:
(begin
(define (foo) 10)
(define (bar) 20))
Can I write macro that will give such output if I use (macro input)? Or the only option is (macro ((foo . 10) (bar . 20)))?
I can accept generic Scheme or Common LISP answer but please don't post define-syntax and hygienic macros from scheme, My lisp don't have them and will never have.
The problem seems to be that I want to access value at macro expansion time and it need to have the value that in runtime. And second question Is eval in this case the only option?
This works in biwascheme:
(define-macro (macro obj)
(let ((obj (eval obj)))
`(begin
,#(map (lambda (pair)
(let ((name (car pair))
(value (cdr pair)))
`(define (,name) ,value)))
obj))))
(define input `((foo . 10) (bar . 20)))
(macro input)
(foo)
;; ==> 10
(bar)
;; ==> 20
(in my lisp eval don't work like in biwascheme but that's other issue).
but this don't work, because x is not global:
(let ((x '((g . 10)))) (macro x))
Is macro with eval something you would normally do, or should them be avoided? Is there other way to generate bunch of functions based on runtime object.
In Common Lisp: creating and compiling functions at runtime.
CL-USER 20 > (defparameter *input* '((foo . 10) (bar . 20)))
*INPUT*
CL-USER 21 > (defun make-my-functions (input)
(loop for (symbol . number) in input
do (compile symbol `(lambda () ,number))))
MAKE-MY-FUNCTIONS
CL-USER 22 > (make-my-functions *input*)
NIL
CL-USER 23 > (foo)
10
CL-USER 24 > (bar)
20
From a local variable:
CL-USER 25 > (let ((input '((foo2 . 102) (bar3 . 303))))
(make-my-functions input))
NIL
CL-USER 26 > (bar3)
303
With a macro, more clumsy and limited:
CL-USER 37 > (defparameter *input* '((foo1 . 101) (bar2 . 202)))
*INPUT*
CL-USER 38 > (defmacro def-my-functions (input &optional getter)
`(progn
,#(loop for (symbol . number) in (if getter
(funcall getter input)
input)
collect `(defun ,symbol () ,number))))
DEF-MY-FUNCTIONS
CL-USER 39 > (def-my-functions *input* symbol-value)
BAR2
CL-USER 40 > (foo1)
101

CLOS slot accessors: read but not write

I have a list of the names of slots of a CLOS object:
(DEFCLASS TRIAL-DATA (STANDARD-OBJECT)
((A-DATUM :ACCESSOR A-DATUM :INITARG :A-DATUM :INITFORM NIL)
(BOTH-DATA :ACCESSOR BOTH-DATA :INITARG :BOTH-DATA :INITFORM 0)
(CUMULATIVE-DATA :ACCESSOR CUMULATIVE-DATA :INITARG :CUMULATIVE-DATA :INITFORM NIL)
(NAME :ACCESSOR NAME :INITARG :NAME :INITFORM VALUE)))
(let* ((td (make-instance 'trial-data))
(slot-lst (mapcar #'slot-definition-name (class-slots (class-of td)))))
I can read the values of these slots:
(let* ((td (make-instance 'trial-data))
(slot-lst (mapcar #'slot-definition-name (class-slots (class-of td)))))
(funcall (symbol-function (nth 0 slot-lst)) td))
==> NIL
But why can I not write new values to these slots? Shouldn't my class definition of trial-data have created an accessor function for each slot?
;; Should set the first slot, a-datum's, value to 42
(let* ((td (make-instance 'trial-data))
(slot-lst (mapcar #'slot-definition-name (class-slots (class-of td)))))
(setf (funcall (symbol-function (nth 0 slot-lst)) td) 42))
==>
;Compiler warnings for "/Users/frank/Documents/NRL/Error/Patrolbot/Patrol Construction Notes & Testing.lisp" :
; In an anonymous lambda form at position 123: Undefined function (SETF FUNCALL)
> Error: Undefined function (SETF FUNCALL) called with arguments (42 #<STANDARD-GENERIC-FUNCTION A-DATUM #x302001D1C5DF> #<TRIAL-DATA #x30200200D95D>) .
> While executing: #<Anonymous Function #x30200200EB7F>, in process Listener-2(5).
The accessor is called a-datum.
The reader:
CL-USER 9 > #'a-datum
#<STANDARD-GENERIC-FUNCTION A-DATUM 406000091C>
The writer:
CL-USER 10 > #'(setf a-datum)
#<STANDARD-GENERIC-FUNCTION (SETF A-DATUM) 422000958C>
If you want to call via funcall the writer, you need to call above function.
If you have a plain form (setf (a-datum foo) 'bar)) then this needs to be resolved at macro expansion time.
The error message says that #'(setf funcall) is undefined. Thus (setf (funcall ...) ...) does not exist.
How do you get the writer function in your case?
CL-USER 11 > (fdefinition '(setf a-datum))
#<STANDARD-GENERIC-FUNCTION (SETF A-DATUM) 422000958C>
CL-USER 12 > (let ((name 'a-datum)) (fdefinition `(setf ,name)))
#<STANDARD-GENERIC-FUNCTION (SETF A-DATUM) 422000958C>
Task for you: what are the correct arguments for above function?
Rainer Joswigs's answer addresses the issue of why you can't set with the code that you have now. However, it's also important to note that there's no reason that reader, writer, or accessor name has to be the same as the slot name, so if what you've actually got is the slot name, then you should use (setf slot-value) with it. E.g.,
(defclass foo ()
((bar :accessor getbar :initform 42)))
(defparameter *foo* (make-instance 'foo))
;; neither of these work
(setf (bar *foo*) 34)
(funcall #'(setf bar) 34 *foo*)
(slot-value *foo* 'bar)
;=> 42
(setf (slot-value *foo* 'bar) 36)
;=> 26
(slot-value *foo* 'bar)
;=> 36

How do i access the :Documentation string of a slot of a Defclass in Common lisp

Ok here is How i instantiate my Defclass and related Defmethod and Defparameter
(defvar *account-numbers* 0)
(defclass bank-account ()
((customer-name
:initarg :customer-name
:initform (error "Must supply a customer name.")
:accessor customer-name
:documentation "Customer's name")
(balance
:initarg :balance
:initform 0
:reader balance
:documentation "Current account balance")
(account-number
:initform (incf *account-numbers*)
:reader account-number
:documentation "Account number, unique within a bank.")
(account-type
:reader account-type
:documentation "Type of account, one of :gold, :silver, or :bronze.")))
(defmethod initialize-instance :after ((account bank-account)
&key opening-bonus-percentage)
(when opening-bonus-percentage
(incf (slot-value account 'balance)
(* (slot-value account 'balance) (/ opening-bonus-percentage 100)))))
(defparameter *account* (make-instance
'bank-account
:customer-name "Ed Monney"
:balance 1000000000
:opening-bonus-percentage 5))
I'm trying to access the :documentation slot-value of any said slot but have not been able to find info in the book i'm reading nor google....
My attempts include
(documentation *account* 'balance)
got this error
WARNING:
unsupported DOCUMENTATION: type BALANCE for object of type BANK-ACCOUNT
I tried
(slot-value bank-account ('balance :documentation))
I got
The variable BANK-ACCOUNT is unbound.
[Condition of type UNBOUND-VARIABLE]
I tried every other variation I could think of slot-value 'balance :documentation 'documentation bank-account and *account* I can think of but just got a lot of different errors any help on learning how to access the :documentation of a defclass slot
is much appreciated
Edit:
#Rainer Joswig that seems to work only right after i entered the defclass at the repl...I was hoping for a way that , if I had a set defclass in a library or something I could just run a command and access the doc. . They way you posted though if i run something else at the repl after the defclass ....I get an error when i run those 4 lines of code....I tried something like (documentation (slot-value account 'balance) t) after i've run my initialize-instance and my defparam account as in my post but get error....could you suggest a way to make the documentation easier to access.
This is not defined in the Common Lisp standard. Unfortunately this is also not beginners territory.
Implementations may provide a way to access the documentation string of a slot.
LispWorks example:
CL-USER 23 > (defclass foo ()
((bar :documentation "this is slot bar in class foo")))
#<STANDARD-CLASS FOO 40200032C3>
CL-USER 24 > (class-slots *)
(#<STANDARD-EFFECTIVE-SLOT-DEFINITION BAR 4020004803>)
CL-USER 25 > (first *)
#<STANDARD-EFFECTIVE-SLOT-DEFINITION BAR 4020004803>
CL-USER 26 > (documentation * 'slot-definition)
"this is slot bar in class foo"
It also works in Clozure CL.
For SBCL it works slightly different.
* (defclass foo ()
((bar :documentation "this is slot bar in class foo")))
#<STANDARD-CLASS FOO>
* (sb-mop:finalize-inheritance *)
NIL
* (sb-mop::class-slots **)
(#<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION BAR>)
* (first *)
#<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION BAR>
* (documentation * t)
"this is slot bar in class foo"

Circular list in Common Lisp

I am working using a visual programming environment for musical composition based on CL . I am trying to create a function that when given say 3 elements (1 2 3) will return 1, 2, 3, 1, 2, 3 etc., one number at the time each time it is evaluated. The book Common Lisp a Gentle Introduction, mentions briefly that it's possible to create circular lists using sharp-equal notation but does not get into details on how to use them.
Keep in mind that I can insert actual Lisp code in the program using a object specifically designed for that.
CL-USER 3 > (defun circular (items)
(setf (cdr (last items)) items)
items)
CIRCULAR
CL-USER 4 > (setf *print-circle* t)
T
CL-USER 5 > (circular (list 1 2 3))
#1=(1 2 3 . #1#)
Example:
CL-USER 16 > (setf c1 (circular (list 1 2 3)))
#1=(1 2 3 . #1#)
CL-USER 17 > (pop c1)
1
CL-USER 18 > (pop c1)
2
CL-USER 19 > (pop c1)
3
CL-USER 20 > (pop c1)
1
also:
CL-USER 6 > '#1=(1 2 3 . #1#)
#1=(1 2 3 . #1#)
With a bit of CLOS added:
(defclass circular ()
((items :initarg :items)))
(defmethod initialize-instance :after ((c circular) &rest initargs)
(setf (slot-value c 'items) (circular (slot-value c 'items))))
(defmethod next-item ((c circular))
(prog1 (first (slot-value c 'items))
(setf (slot-value c 'items)
(rest (slot-value c 'items)))))
CL-USER 7 > (setf circ1 (make-instance 'circular :items (list 1 2 3)))
#<CIRCULAR 40200017CB>
CL-USER 8 > (next-item circ1)
1
CL-USER 9 > (next-item circ1)
2
CL-USER 10 > (next-item circ1)
3
CL-USER 11 > (next-item circ1)
1
CL-USER 12 > (next-item circ1)
2
In Sharpsign Equal-Sign notation, it's written as #0=(1 2 3 . #0#).
Here's a function which creates such a list from the given arguments:
(defun circular (first &rest rest)
(let ((items (cons first rest)))
(setf (cdr (last items)) items)))
Then, calling (circular 1 2 3) will return the circular list you wanted. Just use car and cdr to iterate through the elements ad infinitum.
And if you really want an iterator function that takes no arguments and returns the next item for each call, here's how you might do it:
(defun make-iter (list)
(lambda ()
(pop list)))
First, you want to let the printer know to recognize circular lists instead of trying to print the whole list:
(setf *print-circle* t)
Next, you can create a circular list using the Sharpsign Equal-Sign notation:
(setq x '#1=(1 2 3 . #1#))
Here is an idea worked out for a circular list in Lisp.
;;; Showing structure of the list
;;; (next prev is-end val)
; create items
setf L-0 (L-1 L-3 t "L-0 sentry") ; this will be the sentry item so know where to stop
setf L-1 (L-2 L-0 nil "L-1")
setf L-2 (L-3 L-1 nil "L-2")
setf L-3 (L-0 L-2 nil "L-3")
; how to access L-2 from L-0
eval (first (eval (first L-0)))
; result: (L-3 L-1 NIL "L-2")
I'm not giving defun functions to make adding, removing, and accessing the items. I'm thinking that what I gave is enough to show what you need to do in any functions you define for this kind of circular list. This appeared to me to work in the Listener.

specifying a slot value as a key when removing duplicates

The following code does what I want:
1 (defclass some-class ()
2 ((some-slot
3 :initarg :somearg
4 :initform (error ":somearg not specified"))))
5 (defparameter *alpha* (make-instance 'some-class :somearg 3))
6 (defparameter *beta* (make-instance 'some-class :somearg 5))
7 (defparameter *gamma* (make-instance 'some-class :somearg 3))
8 (princ (slot-value *beta* 'some-slot)) (terpri)
9 (defparameter *delta* (list *alpha* *beta* *gamma*))
10 (princ *delta*) (terpri)
11 (princ (remove-duplicates *delta*
12 :test #'equal
13 :key (lambda (x) (slot-value x 'some-slot))))
14 (terpri)
5
(#<SOME-CLASS #x21C1D71E> #<SOME-CLASS #x21C1DAFE> #<SOME-CLASS #x21C1DC3E>)
(#<SOME-CLASS #x21C1DAFE> #<SOME-CLASS #x21C1DC3E>)
But is there a way to do this without having to write the function on line 13? Is there a shorthand way to specify as the key a slot value in the class instance?
The following blows up with a syntax error, of course, but it gives the general idea of what I'm seeking.
1 (princ (remove-duplicates *delta*
2 :test #'equal
3 :key '(slot-value 'some-slot)))
4 (terpri)
*** - FUNCALL: (SLOT-VALUE 'SOME-SLOT) is not a function name; try using a
symbol instead
You could try a :reader or :accessor.
Doing
(defclass some-class ()
((some-slot
:initarg :somearg :reader some-slot
:initform (error ":somearg not specified"))))
should let you re-write lines 11 through 13 as
(princ (remove-duplicates *delta* :test #'equal :key #'some-slot))
That is, (some-slot x) is equivalent to (slot-value x 'some-slot) if the slot in question has a reader/accessor.
After-Sleep Edit:
You also don't need to bother setting :initform to error; a slot will do that by default if you don't specify a default value and someone tries to read it. If you don't want the error, you do something like :initform nil. Check out this excellent CLOS tutorial as well as chapters 16 and 17 of Practical Common Lisp for more information about objects in Common Lisp.
Also, in the future if you have working code that you'd like style advice on, do check out codereview.stackexchange. There's a small, but active population of Lisp reviewers.
You could define a reader function for the slot in the defclass and provide that as key function to remove-duplicates. For example, add this line to the slot definition:
:reader some-slot
and then use this in the call of remove-duplicates:
:key #'some-slot