Lisp HUG Maillist Archive

Error on macro execution when compiling for the first time

See the code below. 

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call). 
The defclass-object combines both. 

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error. 

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone. 

The error occurs within the test code block. 

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
  `(defmacro ,class-name (&rest initargs) 
     (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
  `(progn
     (defclass ,class-name () ())
     (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time. 

(defparameter *test-code* t)

(when *test-code*
  (defclass-object my-test-class)
  (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation. 
 

#|
First time compiling.
The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).
Then fine. 
|#
;;;;;;;;;

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html

Re: Error on macro execution when compiling for the first time

Minor problem: The code is missing a closing parentheses.

See: http://www.lispworks.com/documentation/HyperSpec/Body/03_bca.htm Processing of Top Level Forms

about top-level-ness: defmacro definitions at top-level will be recognized by the compiler and will be made available in the compile-time environment.

See WHEN: WHEN is at the top-level, but it's subforms are not. Thus the enclosed DEFCLASS-OBJECT form is also not at top-level.
Thus the compiler will expand the macro form, but will not recognize that the generated code has definitions and will not make them available during compilation.

The earlier (defclass-object my-class) is at top-level: it gets expanded and the the generated code is recognized as top-level forms.

Am 07.05.2020 um 17:13 schrieb Bruno Emond <emond.bruno@gmail.com>:

See the code below.

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call).
The defclass-object combines both.

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error.

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone.

The error occurs within the test code block.

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs)
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time.

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation.


#|
First time compiling.
The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).
Then fine.
|#
;;;;;;;;;

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html

Re: Error on macro execution when compiling for the first time



On 7 May 2020, at 17:13, Bruno Emond <emond.bruno@gmail.com> wrote:

See the code below.

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call).
The defclass-object combines both.

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error.

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone.

The error occurs within the test code block.

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs)
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time.

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation.


Yes. 

The problem comes from the fact that the form that define my-test-class is the same form that calls it.  Therefore the compiler cannot generate my-test-class and compile it, before it has to compile the call to my-test-class.


There are several ways to avoir the problem.

The sanest would be to put test code in a test source file, and to have two and system definitions, one for the main code, and another for the test code.  

Then you wouldn’t have to test for *test-code*, testing the program would only involve loading the test and system (which would load the program system, and then run the test).

This would let you write the test as TWO separate top-level forms:

(defclass-object my-test-class)
(my-test-class)

And then the compiler would have a chance to compile the first top-level form, thus expanding the defclass-object macro, and compiling the my-test-class macro.



Another solution would be to realise that my-test-class doesn’t need to be a macro (from the informations you gave us).  Therefore you should expand to a defun form instead.  Then the compiler can generate the code to call the unknown function providing only a warning.  You can avoid this warning by declaring that my-test-class is a function with:

(declaim (ftype (function ()) my-test-class))
(when *test-code*
 (defclass-object my-test-class)
 (my-test-class))


Finally, there’s a brutal way to do it, which is to defer the question to run-time:

(when *test-code*
 (defclass-object my-test-class)
 (eval '(my-test-class)))

But this is really not a good way to do it; the use of EVAL can bring all kinds of difficulties in itself, and it’s really not justified here, since there are better solutions seen above.



The lesson is that when you use macro to define things, you should give them chance to be compiled and executed before the forms that use the results of those macros.

Often, this is done simply by putting the macros (and the functions they use) in a separate source file, that can thus be compiled and loaded before you compile the rest of the program.  The two top-level forms (defclass-object my-test-class) and (my-test-class) can be in separate files.

Also, in some cases, the solution to this kind of problems may involve eval-when, but not here, and you would still have to split the two top-level forms even with eval-when.


-- 
__Pascal J. Bourguignon__




Re: Error on macro execution when compiling for the first time

Rainer, 

Oups, yes the closing parenthesis got removed by error what I added a comment to the code in the email. 

Thank you for the reference to the documentation. 

Bruno


On May 7, 2020, at 11:36, Rainer Joswig <joswig@lisp.de> wrote:

Minor problem: The code is missing a closing parentheses.

See: http://www.lispworks.com/documentation/HyperSpec/Body/03_bca.htm Processing of Top Level Forms

about top-level-ness: defmacro definitions at top-level will be recognized by the compiler and will be made available in the compile-time environment.

See WHEN: WHEN is at the top-level, but it's subforms are not. Thus the enclosed DEFCLASS-OBJECT form is also not at top-level.
Thus the compiler will expand the macro form, but will not recognize that the generated code has definitions and will not make them available during compilation.

The earlier (defclass-object my-class) is at top-level: it gets expanded and the the generated code is recognized as top-level forms.

Am 07.05.2020 um 17:13 schrieb Bruno Emond <emond.bruno@gmail.com>:

See the code below.

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call).
The defclass-object combines both.

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error.

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone.

The error occurs within the test code block.

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs)
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time.

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation.


#|
First time compiling.
The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).
Then fine.
|#
;;;;;;;;;

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html


Re: Error on macro execution when compiling for the first time

Thank you Pascal for your solution options. 

I understand the problem to be rooted with the compilation of macros when they are not at the top-level. 

From the three options you suggested, I think the separate file for tests is the one I prefer as it also follows common software testing practice. 

However, I liked to have the testing code near by, so that in addition to support the actual code testing, I find that it also helps to document function when inspecting the code.
What I usually do is to have the *test-code* parameter at the top of the file and write multiple (when *test-code* …) blocks under each function. Well, almost each function... 
It makes the test code easy available during development and code inspection (no need to go back and forth between files). 
Anyway, it has limitations. 

Thank you for your quick response. 

Bruno

On May 7, 2020, at 11:47, Pascal Bourguignon <pjb@informatimago.com> wrote:



On 7 May 2020, at 17:13, Bruno Emond <emond.bruno@gmail.com> wrote:

See the code below. 

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call). 
The defclass-object combines both. 

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error. 

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone. 

The error occurs within the test code block. 

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs) 
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time. 

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation. 


Yes. 

The problem comes from the fact that the form that define my-test-class is the same form that calls it.  Therefore the compiler cannot generate my-test-class and compile it, before it has to compile the call to my-test-class.


There are several ways to avoir the problem.

The sanest would be to put test code in a test source file, and to have two and system definitions, one for the main code, and another for the test code.  

Then you wouldn’t have to test for *test-code*, testing the program would only involve loading the test and system (which would load the program system, and then run the test).

This would let you write the test as TWO separate top-level forms:

(defclass-object my-test-class)
(my-test-class)

And then the compiler would have a chance to compile the first top-level form, thus expanding the defclass-object macro, and compiling the my-test-class macro.



Another solution would be to realise that my-test-class doesn’t need to be a macro (from the informations you gave us).  Therefore you should expand to a defun form instead.  Then the compiler can generate the code to call the unknown function providing only a warning.  You can avoid this warning by declaring that my-test-class is a function with:

(declaim (ftype (function ()) my-test-class))
(when *test-code*
 (defclass-object my-test-class)
 (my-test-class))


Finally, there’s a brutal way to do it, which is to defer the question to run-time:

(when *test-code*
 (defclass-object my-test-class)
 (eval '(my-test-class)))

But this is really not a good way to do it; the use of EVAL can bring all kinds of difficulties in itself, and it’s really not justified here, since there are better solutions seen above.



The lesson is that when you use macro to define things, you should give them chance to be compiled and executed before the forms that use the results of those macros.

Often, this is done simply by putting the macros (and the functions they use) in a separate source file, that can thus be compiled and loaded before you compile the rest of the program.  The two top-level forms (defclass-object my-test-class) and (my-test-class) can be in separate files.

Also, in some cases, the solution to this kind of problems may involve eval-when, but not here, and you would still have to split the two top-level forms even with eval-when.


-- 
__Pascal J. Bourguignon__

Re: Error on macro execution when compiling for the first time

Pascal and Rainer, 

There are probably many lisp unit testing out there, in particular LISP-UNIT available through QuickLisp, but I did not search a lot. 

I wrote a simple, and minimalist unit testing set of functions to deal with the problem I had. 
I used the opportunity to familiarize myself with conditions. 
It works when the code is compiled for the first time, and it allows me to keep the tests with the code (or move it to another file), and I do not need to use special predicates for testing. 

Anyhow, just sharing it. 
Maybe there are ways to make it even smaller :-)

Bruno

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; unit testing
(defparameter *unit-tests* nil)
(defparameter *unit-test-position* 0)

(define-condition test (simple-condition)
  ((position :initarg :position :initform 0 :accessor test-position)))
(define-condition test-failure (test) ())
(define-condition test-success (test) ())

(defmethod trace-test ((condition test-failure))
  (format t ";;; FAILURE -> Test ~S.~%" (test-position condition)))
(defmethod trace-test ((condition test-success))
  (format t ";;; Success -> Test ~S.~%" (test-position condition)))

(defun clear-unit-tests ()
  (setf *unit-tests* nil))

(defstruct unit-test name code)

(defmacro defunit-test (name &body body)
  `(if (member ,name *unit-tests* :key #'unit-test-name)
       (error "Unit test ~S is already defined." ,name)
     (push (make-unit-test :name ,name :code ',body) *unit-tests*)))

(defun make-function (body)
  (eval `#'(lambda () ,@body)))

(defmethod run-unit-test ((unit-test unit-test))
  (let ((*unit-test-position* 0))
    (handler-bind ((test-failure #'trace-test)
                   (test-success #'trace-test))
      (funcall (make-function (unit-test-code unit-test))))))

(defun test (expression)
  (if expression 
      (signal (make-instance 'test-success :position (incf *unit-test-position*)))
    (signal (make-instance 'test-failure :position (incf *unit-test-position*))))
  expression)

(defmethod run-unit-tests ()
  (format t "~%;;;;;; UNIT TESTS RESULTS.~%" )
  (dolist (unit-test (reverse *unit-tests*) t)
    (format t "~%;;;;;; ~S.~%" (unit-test-name unit-test))
    (run-unit-test unit-test)))
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(clear-unit-tests)

(defmacro make-instance-macro-call (class-name)
  `(defmacro ,class-name (&rest initargs) 
     (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
  `(progn
     (defclass ,class-name () ())
     (make-instance-macro-call ,class-name)))

(defunit-test 'my-unit-test
  (defclass-object my-test-class)
  (my-test-class)
  (test (equal 2 3))
  (test (equal 3 3)))

(defunit-test 'my-second-unit-test
  (test (> 10 9)))

(run-unit-tests)


;;;;;; UNIT TESTS RESULTS.

;;;;;; MY-UNIT-TEST.
;;; FAILURE -> Test 1.
;;; Success -> Test 2.

;;;;;; MY-SECOND-UNIT-TEST.
;;; Success -> Test 1.

On May 7, 2020, at 14:18, Bruno Emond <emond.bruno@gmail.com> wrote:

Thank you Pascal for your solution options. 

I understand the problem to be rooted with the compilation of macros when they are not at the top-level. 

From the three options you suggested, I think the separate file for tests is the one I prefer as it also follows common software testing practice. 

However, I liked to have the testing code near by, so that in addition to support the actual code testing, I find that it also helps to document function when inspecting the code.
What I usually do is to have the *test-code* parameter at the top of the file and write multiple (when *test-code* …) blocks under each function. Well, almost each function... 
It makes the test code easy available during development and code inspection (no need to go back and forth between files). 
Anyway, it has limitations. 

Thank you for your quick response. 

Bruno

On May 7, 2020, at 11:47, Pascal Bourguignon <pjb@informatimago.com> wrote:



On 7 May 2020, at 17:13, Bruno Emond <emond.bruno@gmail.com> wrote:

See the code below. 

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call). 
The defclass-object combines both. 

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error. 

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone. 

The error occurs within the test code block. 

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs) 
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time. 

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation. 


Yes. 

The problem comes from the fact that the form that define my-test-class is the same form that calls it.  Therefore the compiler cannot generate my-test-class and compile it, before it has to compile the call to my-test-class.


There are several ways to avoir the problem.

The sanest would be to put test code in a test source file, and to have two and system definitions, one for the main code, and another for the test code.  

Then you wouldn’t have to test for *test-code*, testing the program would only involve loading the test and system (which would load the program system, and then run the test).

This would let you write the test as TWO separate top-level forms:

(defclass-object my-test-class)
(my-test-class)

And then the compiler would have a chance to compile the first top-level form, thus expanding the defclass-object macro, and compiling the my-test-class macro.



Another solution would be to realise that my-test-class doesn’t need to be a macro (from the informations you gave us).  Therefore you should expand to a defun form instead.  Then the compiler can generate the code to call the unknown function providing only a warning.  You can avoid this warning by declaring that my-test-class is a function with:

(declaim (ftype (function ()) my-test-class))
(when *test-code*
 (defclass-object my-test-class)
 (my-test-class))


Finally, there’s a brutal way to do it, which is to defer the question to run-time:

(when *test-code*
 (defclass-object my-test-class)
 (eval '(my-test-class)))

But this is really not a good way to do it; the use of EVAL can bring all kinds of difficulties in itself, and it’s really not justified here, since there are better solutions seen above.



The lesson is that when you use macro to define things, you should give them chance to be compiled and executed before the forms that use the results of those macros.

Often, this is done simply by putting the macros (and the functions they use) in a separate source file, that can thus be compiled and loaded before you compile the rest of the program.  The two top-level forms (defclass-object my-test-class) and (my-test-class) can be in separate files.

Also, in some cases, the solution to this kind of problems may involve eval-when, but not here, and you would still have to split the two top-level forms even with eval-when.


-- 
__Pascal J. Bourguignon__


Re: Error on macro execution when compiling for the first time

Somewhat offtopic: There are many, many testing frameworks available: LISP-UNIT, 1AM, 2AM, FiveAM, Prove, Rove, CLUnit, Parachute, LIFT, the list goes on. Some people consider that list to be way too long, possibly since it's trivial to write a test framework in Lisp.

See https://cliki.net/Test%20Framework for an example list.

On 08.05.2020 21:09, Bruno Emond wrote:
Pascal and Rainer, 

There are probably many lisp unit testing out there, in particular LISP-UNIT available through QuickLisp, but I did not search a lot. 

I wrote a simple, and minimalist unit testing set of functions to deal with the problem I had. 
I used the opportunity to familiarize myself with conditions. 
It works when the code is compiled for the first time, and it allows me to keep the tests with the code (or move it to another file), and I do not need to use special predicates for testing. 

Anyhow, just sharing it. 
Maybe there are ways to make it even smaller :-)

Bruno

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; unit testing
(defparameter *unit-tests* nil)
(defparameter *unit-test-position* 0)

(define-condition test (simple-condition)
  ((position :initarg :position :initform 0 :accessor test-position)))
(define-condition test-failure (test) ())
(define-condition test-success (test) ())

(defmethod trace-test ((condition test-failure))
  (format t ";;; FAILURE -> Test ~S.~%" (test-position condition)))
(defmethod trace-test ((condition test-success))
  (format t ";;; Success -> Test ~S.~%" (test-position condition)))

(defun clear-unit-tests ()
  (setf *unit-tests* nil))

(defstruct unit-test name code)

(defmacro defunit-test (name &body body)
  `(if (member ,name *unit-tests* :key #'unit-test-name)
       (error "Unit test ~S is already defined." ,name)
     (push (make-unit-test :name ,name :code ',body) *unit-tests*)))

(defun make-function (body)
  (eval `#'(lambda () ,@body)))

(defmethod run-unit-test ((unit-test unit-test))
  (let ((*unit-test-position* 0))
    (handler-bind ((test-failure #'trace-test)
                   (test-success #'trace-test))
      (funcall (make-function (unit-test-code unit-test))))))

(defun test (expression)
  (if expression 
      (signal (make-instance 'test-success :position (incf *unit-test-position*)))
    (signal (make-instance 'test-failure :position (incf *unit-test-position*))))
  expression)

(defmethod run-unit-tests ()
  (format t "~%;;;;;; UNIT TESTS RESULTS.~%" )
  (dolist (unit-test (reverse *unit-tests*) t)
    (format t "~%;;;;;; ~S.~%" (unit-test-name unit-test))
    (run-unit-test unit-test)))
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(clear-unit-tests)

(defmacro make-instance-macro-call (class-name)
  `(defmacro ,class-name (&rest initargs) 
     (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
  `(progn
     (defclass ,class-name () ())
     (make-instance-macro-call ,class-name)))

(defunit-test 'my-unit-test
  (defclass-object my-test-class)
  (my-test-class)
  (test (equal 2 3))
  (test (equal 3 3)))

(defunit-test 'my-second-unit-test
  (test (> 10 9)))

(run-unit-tests)


;;;;;; UNIT TESTS RESULTS.

;;;;;; MY-UNIT-TEST.
;;; FAILURE -> Test 1.
;;; Success -> Test 2.

;;;;;; MY-SECOND-UNIT-TEST.
;;; Success -> Test 1.

On May 7, 2020, at 14:18, Bruno Emond <emond.bruno@gmail.com> wrote:

Thank you Pascal for your solution options. 

I understand the problem to be rooted with the compilation of macros when they are not at the top-level. 

From the three options you suggested, I think the separate file for tests is the one I prefer as it also follows common software testing practice. 

However, I liked to have the testing code near by, so that in addition to support the actual code testing, I find that it also helps to document function when inspecting the code.
What I usually do is to have the *test-code* parameter at the top of the file and write multiple (when *test-code* …) blocks under each function. Well, almost each function... 
It makes the test code easy available during development and code inspection (no need to go back and forth between files). 
Anyway, it has limitations. 

Thank you for your quick response. 

Bruno

On May 7, 2020, at 11:47, Pascal Bourguignon <pjb@informatimago.com> wrote:



On 7 May 2020, at 17:13, Bruno Emond <emond.bruno@gmail.com> wrote:

See the code below. 

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call). 
The defclass-object combines both. 

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error. 

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone. 

The error occurs within the test code block. 

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs) 
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time. 

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation. 


Yes. 

The problem comes from the fact that the form that define my-test-class is the same form that calls it.  Therefore the compiler cannot generate my-test-class and compile it, before it has to compile the call to my-test-class.


There are several ways to avoir the problem.

The sanest would be to put test code in a test source file, and to have two and system definitions, one for the main code, and another for the test code.  

Then you wouldn’t have to test for *test-code*, testing the program would only involve loading the test and system (which would load the program system, and then run the test).

This would let you write the test as TWO separate top-level forms:

(defclass-object my-test-class)
(my-test-class)

And then the compiler would have a chance to compile the first top-level form, thus expanding the defclass-object macro, and compiling the my-test-class macro.



Another solution would be to realise that my-test-class doesn’t need to be a macro (from the informations you gave us).  Therefore you should expand to a defun form instead.  Then the compiler can generate the code to call the unknown function providing only a warning.  You can avoid this warning by declaring that my-test-class is a function with:

(declaim (ftype (function ()) my-test-class))
(when *test-code*
 (defclass-object my-test-class)
 (my-test-class))


Finally, there’s a brutal way to do it, which is to defer the question to run-time:

(when *test-code*
 (defclass-object my-test-class)
 (eval '(my-test-class)))

But this is really not a good way to do it; the use of EVAL can bring all kinds of difficulties in itself, and it’s really not justified here, since there are better solutions seen above.



The lesson is that when you use macro to define things, you should give them chance to be compiled and executed before the forms that use the results of those macros.

Often, this is done simply by putting the macros (and the functions they use) in a separate source file, that can thus be compiled and loaded before you compile the rest of the program.  The two top-level forms (defclass-object my-test-class) and (my-test-class) can be in separate files.

Also, in some cases, the solution to this kind of problems may involve eval-when, but not here, and you would still have to split the two top-level forms even with eval-when.


-- 
__Pascal J. Bourguignon__


Re: Error on macro execution when compiling for the first time

Michal, 
Thanks for the link. 
Definitely not off topic. 
I agree with your assessment that there is probably no need for yet another testing framework. 
Bruno

On May 8, 2020, at 15:16, Michał phoe Herda <phoe@disroot.org> wrote:

Somewhat offtopic: There are many, many testing frameworks available: LISP-UNIT, 1AM, 2AM, FiveAM, Prove, Rove, CLUnit, Parachute, LIFT, the list goes on. Some people consider that list to be way too long, possibly since it's trivial to write a test framework in Lisp.

See https://cliki.net/Test%20Framework for an example list.

On 08.05.2020 21:09, Bruno Emond wrote:
Pascal and Rainer, 

There are probably many lisp unit testing out there, in particular LISP-UNIT available through QuickLisp, but I did not search a lot. 

I wrote a simple, and minimalist unit testing set of functions to deal with the problem I had. 
I used the opportunity to familiarize myself with conditions. 
It works when the code is compiled for the first time, and it allows me to keep the tests with the code (or move it to another file), and I do not need to use special predicates for testing. 

Anyhow, just sharing it. 
Maybe there are ways to make it even smaller :-)

Bruno

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; unit testing
(defparameter *unit-tests* nil)
(defparameter *unit-test-position* 0)

(define-condition test (simple-condition)
  ((position :initarg :position :initform 0 :accessor test-position)))
(define-condition test-failure (test) ())
(define-condition test-success (test) ())

(defmethod trace-test ((condition test-failure))
  (format t ";;; FAILURE -> Test ~S.~%" (test-position condition)))
(defmethod trace-test ((condition test-success))
  (format t ";;; Success -> Test ~S.~%" (test-position condition)))

(defun clear-unit-tests ()
  (setf *unit-tests* nil))

(defstruct unit-test name code)

(defmacro defunit-test (name &body body)
  `(if (member ,name *unit-tests* :key #'unit-test-name)
       (error "Unit test ~S is already defined." ,name)
     (push (make-unit-test :name ,name :code ',body) *unit-tests*)))

(defun make-function (body)
  (eval `#'(lambda () ,@body)))

(defmethod run-unit-test ((unit-test unit-test))
  (let ((*unit-test-position* 0))
    (handler-bind ((test-failure #'trace-test)
                   (test-success #'trace-test))
      (funcall (make-function (unit-test-code unit-test))))))

(defun test (expression)
  (if expression 
      (signal (make-instance 'test-success :position (incf *unit-test-position*)))
    (signal (make-instance 'test-failure :position (incf *unit-test-position*))))
  expression)

(defmethod run-unit-tests ()
  (format t "~%;;;;;; UNIT TESTS RESULTS.~%" )
  (dolist (unit-test (reverse *unit-tests*) t)
    (format t "~%;;;;;; ~S.~%" (unit-test-name unit-test))
    (run-unit-test unit-test)))
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(clear-unit-tests)

(defmacro make-instance-macro-call (class-name)
  `(defmacro ,class-name (&rest initargs) 
     (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
  `(progn
     (defclass ,class-name () ())
     (make-instance-macro-call ,class-name)))

(defunit-test 'my-unit-test
  (defclass-object my-test-class)
  (my-test-class)
  (test (equal 2 3))
  (test (equal 3 3)))

(defunit-test 'my-second-unit-test
  (test (> 10 9)))

(run-unit-tests)


;;;;;; UNIT TESTS RESULTS.

;;;;;; MY-UNIT-TEST.
;;; FAILURE -> Test 1.
;;; Success -> Test 2.

;;;;;; MY-SECOND-UNIT-TEST.
;;; Success -> Test 1.

On May 7, 2020, at 14:18, Bruno Emond <emond.bruno@gmail.com> wrote:

Thank you Pascal for your solution options. 

I understand the problem to be rooted with the compilation of macros when they are not at the top-level. 

From the three options you suggested, I think the separate file for tests is the one I prefer as it also follows common software testing practice. 

However, I liked to have the testing code near by, so that in addition to support the actual code testing, I find that it also helps to document function when inspecting the code.
What I usually do is to have the *test-code* parameter at the top of the file and write multiple (when *test-code* …) blocks under each function. Well, almost each function... 
It makes the test code easy available during development and code inspection (no need to go back and forth between files). 
Anyway, it has limitations. 

Thank you for your quick response. 

Bruno

On May 7, 2020, at 11:47, Pascal Bourguignon <pjb@informatimago.com> wrote:



On 7 May 2020, at 17:13, Bruno Emond <emond.bruno@gmail.com> wrote:

See the code below. 

I wrote a macro to define a class, and another one to make a simple call to create an instance of the class (make-instance-macro-call). 
The defclass-object combines both. 

As a means to test my code, I often write test functions that execute only if I want to test the code or not (see parameter *test-code*).

If I load the code, then compile there is no error. 

The first time I compile the code, I get the following error:

The call (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC>) does not match definition (#<Function 1 subfunction of (SUBFUNCTION # #) 413003DAFC> &REST INITARGS).

When aborting, and recompiling, the error is gone. 

The error occurs within the test code block. 

I am just wondering. Is there a way to avoid this error when the code gets compiled for the first time?

Bruno

;;;;;;;
(defmacro make-instance-macro-call (class-name)
 `(defmacro ,class-name (&rest initargs) 
    (append (list 'make-instance '',class-name) initargs)))

(defmacro defclass-object (class-name)
 `(progn
    (defclass ,class-name () ())
    (make-instance-macro-call ,class-name)))

(defclass-object my-class)
(my-class) ;; this creates an instance of my-class. No issue when compiling for the fist time. 

(defparameter *test-code* t)

(when *test-code*
 (defclass-object my-test-class)
 (my-test-class) ;; This generates an error the first time it is compiled, then it is fine on subsequent compilation. 


Yes. 

The problem comes from the fact that the form that define my-test-class is the same form that calls it.  Therefore the compiler cannot generate my-test-class and compile it, before it has to compile the call to my-test-class.


There are several ways to avoir the problem.

The sanest would be to put test code in a test source file, and to have two and system definitions, one for the main code, and another for the test code.  

Then you wouldn’t have to test for *test-code*, testing the program would only involve loading the test and system (which would load the program system, and then run the test).

This would let you write the test as TWO separate top-level forms:

(defclass-object my-test-class)
(my-test-class)

And then the compiler would have a chance to compile the first top-level form, thus expanding the defclass-object macro, and compiling the my-test-class macro.



Another solution would be to realise that my-test-class doesn’t need to be a macro (from the informations you gave us).  Therefore you should expand to a defun form instead.  Then the compiler can generate the code to call the unknown function providing only a warning.  You can avoid this warning by declaring that my-test-class is a function with:

(declaim (ftype (function ()) my-test-class))
(when *test-code*
 (defclass-object my-test-class)
 (my-test-class))


Finally, there’s a brutal way to do it, which is to defer the question to run-time:

(when *test-code*
 (defclass-object my-test-class)
 (eval '(my-test-class)))

But this is really not a good way to do it; the use of EVAL can bring all kinds of difficulties in itself, and it’s really not justified here, since there are better solutions seen above.



The lesson is that when you use macro to define things, you should give them chance to be compiled and executed before the forms that use the results of those macros.

Often, this is done simply by putting the macros (and the functions they use) in a separate source file, that can thus be compiled and loaded before you compile the rest of the program.  The two top-level forms (defclass-object my-test-class) and (my-test-class) can be in separate files.

Also, in some cases, the solution to this kind of problems may involve eval-when, but not here, and you would still have to split the two top-level forms even with eval-when.


-- 
__Pascal J. Bourguignon__



Updated at: 2020-12-10 08:28 UTC