A "declare" form returned by macro

Discussion of Common Lisp
TPJ
Posts: 11
Joined: Tue Nov 25, 2008 6:37 am
Location: Gliwice, Poland

A "declare" form returned by macro

Post by TPJ » Tue Dec 30, 2008 5:23 am

After xmas, I started to write my first code in Common Lisp. I started with the following function:

Code: Select all

(defun fixvect (f_len f_initEl)
  (declare (type fixnum f_len)
           (type fixnum f_initEl))
  (make-array (list f_len) :element-type 'fixnum :initial-element f_initEl))
Of course, the function above is supposed to make a new 1-d array of fixnum values, filled with the given initial element.

Soon it becomes obvious to me, that I'll use the declare forms a lot in my code. So, I decided to write the following macro:

Code: Select all

(defmacro decltype (&rest lst)
  (defun type-collect (lst)
    ;;; (some-type a b c) -> ( (type some-type a) (type some-type b)
    ;;;                        (type some-type c) )
    (let ( (typ (car lst))
           (vars (cdr lst)) )
      (loop for var in vars collect `(type ,typ ,var))))
  (let ( (type-list nil) )
    (loop for lstInt in lst do
      (setf type-list (append type-list (type-collect lstInt))))
    `(declare ,@type-list)))
Now, I could replace the form:

Code: Select all

(declare (type fixnum f_len)
         (type fixnum f_initEl))
with the following line:

Code: Select all

(decltype (fixnum f_len f_initEl))
At least, I thought so... Common Lisp compiler told me I was doing something wrong:

Code: Select all

; in: LAMBDA NIL
;     (DECLTYPE (FIXNUM F_LEN F_INITEL))
; 
; caught STYLE-WARNING:
;   (in macroexpansion of (DECLTYPE (FIXNUM F_LEN F_INITEL)))
;   (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
;   redefining TYPE-COLLECT in DEFUN

; in: LAMBDA NIL
;     (DECLTYPE (FIXNUM F_LEN F_INITEL))
; ==>
;   (DECLARE (TYPE FIXNUM F_LEN) (TYPE FIXNUM F_INITEL))
; 
; caught WARNING:
;   There is no function named DECLARE. References to DECLARE in some contexts
;   (like starts of blocks) have special meaning, but here it would have to be a
;   function, and that shouldn't be right.

; --> DECLARE 
; ==>
;   (TYPE FIXNUM F_LEN)
; 
; caught WARNING:
;   undefined variable: FIXNUM
; 
; caught WARNING:
;   The function TYPE is undefined, and its name is reserved by ANSI CL so that
;   even if it were defined later, the code doing so would not be portable.

; 
; caught WARNING:
;   This variable is undefined:
;     FIXNUM

; 
; caught STYLE-WARNING:
;   These functions are undefined:
;     DECLARE TYPE
; 
; compilation unit finished
;   caught 4 WARNING conditions
;   caught 2 STYLE-WARNING conditions
I have to admit, that I don't get it. AFAIK (or, to be more precise, as far as I get the concept of macros in CL), the macro is expanded to some form at the compile time, and that form is then evaluated. The macro I wrote expands to the right form (I checked it with macroexpand-1), so what's the problem, mr CL compiler?

Perhaps the problem is that the "declare" form shouldn't be evaluated (it's an information for CL compiler), and CL compiler tries to evaluate it, so both "declare" and "type" are treated as functions. If this is the case, how can I define syntax for (decltype (fixnum var*))?

dmitry_vk
Posts: 96
Joined: Sat Jun 28, 2008 8:01 am
Location: Russia, Kazan
Contact:

Re: A "declare" form returned by macro

Post by dmitry_vk » Tue Dec 30, 2008 5:33 am

TPJ wrote:how can I define syntax for (decltype (fixnum var*))?
The thing is that you do not need define this syntax. You can just use (declare (type fixnum var*)) (details at http://www.lispworks.com/documentation/ ... d_type.htm).

TPJ
Posts: 11
Joined: Tue Nov 25, 2008 6:37 am
Location: Gliwice, Poland

Re: A "declare" form returned by macro

Post by TPJ » Tue Dec 30, 2008 6:36 am

Yeap. I'm studying the CLtL, but I obviously missed that information. Now I feel like a silly...

Thanks for your help.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: A "declare" form returned by macro

Post by ramarren » Tue Dec 30, 2008 7:18 am

The specifications specifically prohibits macros expanding into declarations:
http://www.lispworks.com/documentation/ ... declar.htm

Code: Select all

Macro forms cannot expand into declarations; declare expressions must appear as actual subexpressions of the form to which they refer. 
I think this is because declaration can affect how the rest of the form will be compiled, and so they must be there before arguments are macroexpanded.

Paul Donnelly
Posts: 148
Joined: Wed Jul 30, 2008 11:26 pm

Re: A "declare" form returned by macro

Post by Paul Donnelly » Tue Dec 30, 2008 12:32 pm

Altering your macro to something like the following is the best you can do, I think. But since the declarations will apply to the LET form in the macro rather than to the function itself, I don't know how much good it will do if you're adding those for speed.

Code: Select all

(defmacro with-declare (declarations &body body)
  `(let ,(loop for a in declarations
            append (loop for b in (cdr a)
                        collect (list b b)))
     (declare ,@(loop for a in declarations
                   append (loop for b in (cdr a)
                              with type = (car a)
                              collect (list 'type type b))))
     ,@body))

(macroexpand-1 '(with-declare ((fixnum a b) (string c )) (+ a b) (print c)))
==>
(LET ((A A) (B B) (C C))
  (DECLARE (TYPE FIXNUM A) (TYPE FIXNUM B) (TYPE STRING C))
  (+ A B)
  (PRINT C))

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: A "declare" form returned by macro

Post by nuntius » Tue Dec 30, 2008 6:09 pm

Two approaches I know of are

read macros (including #.)

Code: Select all

(defun f (x) #.(decltype (fixnum x)) (+ x 2))
custom wrappers around def*

Code: Select all

(defmacro add-decl (decl form)
  "Insert DECL into FORM"
  (destructuring-bind (type name args &body body) form
    (list type name args
      (eval decl) ; or macroexpand
      body)))

(add-decl 
  (decltype (fixnum x))
  (defun f (x) (+ x 2)))
This code is untested, but the idea is sound.

Both approaches can be designed for macros, functions, or even *special-variables*.
See also tools like http://common-lisp.net/project/parse-declarations/.

Paul Donnelly
Posts: 148
Joined: Wed Jul 30, 2008 11:26 pm

Re: A "declare" form returned by macro

Post by Paul Donnelly » Tue Dec 30, 2008 8:47 pm

Of course you can also make a wrapper for DEFUN that includes syntax for type annotations.

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: A "declare" form returned by macro

Post by findinglisp » Thu Jan 01, 2009 11:32 pm

In addition to all the other fine suggestions about how you can ease your pain in declaring types, might I suggest that you simply don't declare types at all. If you're really trying to write your first code in Common Lisp, the last thing you'll want to spend any time on is typing. Lisp doesn't require typing, so why use it? Yes, yes, I know performance, blah, blah. But come on. This is your first code. Figure out the language. Get it to run. Then worry about whether it's running fast enough. Trust me, if this really is your first program in CL, you'll be consing so much as to make all your type declarations all but irrelevant anyway. :D
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Paul Donnelly
Posts: 148
Joined: Wed Jul 30, 2008 11:26 pm

Re: A "declare" form returned by macro

Post by Paul Donnelly » Fri Jan 02, 2009 4:39 pm

findinglisp wrote:In addition to all the other fine suggestions about how you can ease your pain in declaring types, might I suggest that you simply don't declare types at all.
Amen to that. Most of the time, type declarations are pointless.

dlweinreb
Posts: 41
Joined: Tue Jul 01, 2008 5:11 am
Location: Lexington, MA
Contact:

Re: A "declare" form returned by macro

Post by dlweinreb » Tue Jan 06, 2009 4:18 am

Although it does not work to have a macro that expands directly into a (declare ...),
you can do this kind of thing:

(defmacro with-dcl (var &body body)
`(let ((,var 0))
(declare (fixnum ,var))
,@body))

Post Reply