From: Martin Raspaud
Subject: my-defun
Date: 
Message-ID: <ca4s00$3rc$1@news.u-bordeaux.fr>
Hi all,

I would like to create a "my-defun" macro to replace in some way defun.
"my-defun" would take a specialized lambda list instead of a regular 
lambda list as argument, so that

(my-defun any-name ((fix fixnum) (db double-float) other-arg)
   body)

would expand in something like

(defun any-name (fix db other-arg)
   (declare (type fixnum fix)
            (type double-float db))
   body)

I got something but documentation string, keywords, optionals and so on 
get in the way.

The way I started was :

(defmacro defun-quick (function-name specialized-lambda-list &body body)
   (let ((mac-lambda-list (loop for i in specialized-lambda-list
			       collect (if (consp i)
					   (car i)
					   i)))
	(mac-declare-list (loop for i in specialized-lambda-list
				when (consp i)
			       collect (list 'type (cadr i) (car i)))))
					
     `(defun ,function-name ,mac-lambda-list
       (declare ,@mac-declare-list)
     ,@body)))

but I guess there must be a more elegant way. (actually this macro even 
doesn't work if I don't specialize an argument in the lambda list, which 
is something I need)...

Thanks for any help

Martin

From: Peter Seibel
Subject: Re: my-defun
Date: 
Message-ID: <m3oenux7wa.fsf@javamonkey.com>
Martin Raspaud <········@labri.fr> writes:

> Hi all,
>
> I would like to create a "my-defun" macro to replace in some way defun.
> "my-defun" would take a specialized lambda list instead of a regular
> lambda list as argument, so that
>
> (my-defun any-name ((fix fixnum) (db double-float) other-arg)
>    body)
>
> would expand in something like
>
> (defun any-name (fix db other-arg)
>    (declare (type fixnum fix)
>             (type double-float db))
>    body)
>
> I got something but documentation string, keywords, optionals and so
> on get in the way.
>
> The way I started was :
>
> (defmacro defun-quick (function-name specialized-lambda-list &body body)
>    (let ((mac-lambda-list (loop for i in specialized-lambda-list
> 			       collect (if (consp i)
> 					   (car i)
> 					   i)))
> 	(mac-declare-list (loop for i in specialized-lambda-list
> 				when (consp i)
> 			       collect (list 'type (cadr i) (car i)))))
> 					
>      `(defun ,function-name ,mac-lambda-list
>        (declare ,@mac-declare-list)
>      ,@body)))
>
> but I guess there must be a more elegant way. (actually this macro
> even doesn't work if I don't specialize an argument in the lambda
> list, which is something I need)...
>
> Thanks for any help

How about something like this:

(defmacro my-defun (name lambda-list &body body)
  (loop for (param . rest) on lambda-list
        when (symbolp param)
          collect param into required-parameters
        else
          collect (first param) into required-parameters and
          collect `(type ,(second param) ,(first param)) into declarations
        until (member param lambda-list-keywords)
        finally (return
                  `(defun ,name ,(nconc required-parameters rest)
                    ,@(if declarations `((declare ,@declarations)))
                    ,@body))))

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Andreas Scholta
Subject: Re: my-defun
Date: 
Message-ID: <87fz95y5ah.fsf@solace.kozmoz.org>
Peter Seibel <·····@javamonkey.com> writes:

> How about something like this:
>
> (defmacro my-defun (name lambda-list &body body)
>   (loop for (param . rest) on lambda-list
>         when (symbolp param)
>           collect param into required-parameters
>         else
>           collect (first param) into required-parameters and
>           collect `(type ,(second param) ,(first param)) into declarations
>         until (member param lambda-list-keywords)
>         finally (return
>                   `(defun ,name ,(nconc required-parameters rest)
>                     ,@(if declarations `((declare ,@declarations)))
>                     ,@body))))
>

Or how about an alternative to loop resembling this ugly piece of code:

(defmacro my-defun (name args &body body)
  (labels ((split-arglist (args normal tds)
             (if (or (null args)
                     (member (car args)
                             lambda-list-keywords))
                 (values (nreverse normal)
                         (nreverse tds)
                         args)
                 (let ((arg (car args)))
                   (if (symbolp arg)
                       (split-arglist (cdr args)
                                      (cons arg normal)
                                      tds)
                       (split-arglist (cdr args)
                                      (cons (car arg) normal)
                                      (cons `(type ,@(nreverse arg))
                                            tds)))))))
    (multiple-value-bind (nargs tds rest)
        (split-arglist args nil nil)
      `(defun ,name ,(nconc nargs rest)
         ,(if tds `(declare ,@tds))
         ,@body))))

-- 
cheers,
    Andreas Scholta
From: Raspaud Martin
Subject: Re: my-defun
Date: 
Message-ID: <40fd2f15$0$29366$626a14ce@news.free.fr>
Peter Seibel wrote:
> Martin Raspaud <········@labri.fr> writes:
> 
> 
>>Hi all,
>>
>>I would like to create a "my-defun" macro to replace in some way defun.
>>"my-defun" would take a specialized lambda list instead of a regular
>>lambda list as argument, so that
>>
>>(my-defun any-name ((fix fixnum) (db double-float) other-arg)
>>   body)
>>
>>would expand in something like
>>
>>(defun any-name (fix db other-arg)
>>   (declare (type fixnum fix)
>>            (type double-float db))
>>   body)
>>
>>I got something but documentation string, keywords, optionals and so
>>on get in the way.
>>
>>The way I started was :
>>
>>(defmacro defun-quick (function-name specialized-lambda-list &body body)
>>   (let ((mac-lambda-list (loop for i in specialized-lambda-list
>>			       collect (if (consp i)
>>					   (car i)
>>					   i)))
>>	(mac-declare-list (loop for i in specialized-lambda-list
>>				when (consp i)
>>			       collect (list 'type (cadr i) (car i)))))
>>					
>>     `(defun ,function-name ,mac-lambda-list
>>       (declare ,@mac-declare-list)
>>     ,@body)))
>>
>>but I guess there must be a more elegant way. (actually this macro
>>even doesn't work if I don't specialize an argument in the lambda
>>list, which is something I need)...
>>
>>Thanks for any help
> 
> 
> How about something like this:
> 
> (defmacro my-defun (name lambda-list &body body)
>   (loop for (param . rest) on lambda-list
>         when (symbolp param)
>           collect param into required-parameters
>         else
>           collect (first param) into required-parameters and
>           collect `(type ,(second param) ,(first param)) into declarations
>         until (member param lambda-list-keywords)
>         finally (return
>                   `(defun ,name ,(nconc required-parameters rest)
>                     ,@(if declarations `((declare ,@declarations)))
>                     ,@body))))
> 

Thanks for the answer... I've been using it since and it works really 
well...

Now is it possible to enhance this so that the macro generates also 
(before the defun) something like
(declaim (ftype (function (<types if specified, t otherwise>) a-type) 
name)))
?

I've been trying but I'm getting stuck with the keywords again.

By the way is there a page explaining how this all works (I mean lambda 
list parsing and keyword handling and so on) ?

Thanks

Martin.
From: Peter Seibel
Subject: Re: my-defun
Date: 
Message-ID: <m38ydeg24c.fsf@javamonkey.com>
Raspaud Martin <········@free.fr> writes:

> Peter Seibel wrote:
>> Martin Raspaud <········@labri.fr> writes:
>>
>>>Hi all,
>>>
>>>I would like to create a "my-defun" macro to replace in some way defun.
>>>"my-defun" would take a specialized lambda list instead of a regular
>>>lambda list as argument, so that
>>>
>>>(my-defun any-name ((fix fixnum) (db double-float) other-arg)
>>>   body)
>>>
>>>would expand in something like
>>>
>>>(defun any-name (fix db other-arg)
>>>   (declare (type fixnum fix)
>>>            (type double-float db))
>>>   body)
>>>
>>>I got something but documentation string, keywords, optionals and so
>>>on get in the way.
>>>
>>>The way I started was :
>>>
>>>(defmacro defun-quick (function-name specialized-lambda-list &body body)
>>>   (let ((mac-lambda-list (loop for i in specialized-lambda-list
>>>			       collect (if (consp i)
>>>					   (car i)
>>>					   i)))
>>>	(mac-declare-list (loop for i in specialized-lambda-list
>>>				when (consp i)
>>>			       collect (list 'type (cadr i) (car i)))))
>>>					
>>>     `(defun ,function-name ,mac-lambda-list
>>>       (declare ,@mac-declare-list)
>>>     ,@body)))
>>>
>>>but I guess there must be a more elegant way. (actually this macro
>>>even doesn't work if I don't specialize an argument in the lambda
>>>list, which is something I need)...
>>>
>>>Thanks for any help
>> How about something like this:
>> (defmacro my-defun (name lambda-list &body body)
>>   (loop for (param . rest) on lambda-list
>>         when (symbolp param)
>>           collect param into required-parameters
>>         else
>>           collect (first param) into required-parameters and
>>           collect `(type ,(second param) ,(first param)) into declarations
>>         until (member param lambda-list-keywords)
>>         finally (return
>>                   `(defun ,name ,(nconc required-parameters rest)
>>                     ,@(if declarations `((declare ,@declarations)))
>>                     ,@body))))
>>
>
> Thanks for the answer... I've been using it since and it works really
> well...
>
> Now is it possible to enhance this so that the macro generates also
> (before the defun) something like
> (declaim (ftype (function (<types if specified, t otherwise>) a-type)
> name)))
> ?

Sure. Probably the simplest way is to collect just the type names into
a separate list in the "else" clause of the loop. (And you'll have to
figure out how to specify the return type of your function.

> I've been trying but I'm getting stuck with the keywords again.

I'm not sure what you mean by that. 

> By the way is there a page explaining how this all works (I mean
> lambda list parsing and keyword handling and so on) ?

Look in section 3.4 of the Hyperspec.

-Peter


-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Raspaud Martin
Subject: Re: my-defun
Date: 
Message-ID: <40fd350f$0$23454$626a14ce@news.free.fr>
Peter Seibel wrote:
>>
>>Now is it possible to enhance this so that the macro generates also
>>(before the defun) something like
>>(declaim (ftype (function (<types if specified, t otherwise>) a-type)
>>name)))
>>?
> 
> 
> Sure. Probably the simplest way is to collect just the type names into
> a separate list in the "else" clause of the loop. (And you'll have to
> figure out how to specify the return type of your function.
> 
> 
>>I've been trying but I'm getting stuck with the keywords again.
> 
> 
> I'm not sure what you mean by that. 

Indeed I've been collecting as you said. However the loop macro stop 
whenever it finds a keyword. So how do I handle the rest of the 
parameters ? (like those that follow &key, &rest, &optional and so on)
because if I'm not mistaken the syntax for ftype include the keywords 
also (like [...](function (integer &key double-float))[...])


Here's what I did for now (this is a simple version that does not take 
the types of the parameters into account, thus generating warnings at 
compilation time)

(defmacro typed-defun (type name lambda-list &body body)
   "Redefinition of defun to optimize for speed."
   (loop for (param . rest) on lambda-list
         when (symbolp param)
	  collect param into required-parameters
         else
	  collect (first param)
	  into required-parameters
	  and collect `(type ,(second param) ,(first param))
	  into declarations
         until (member param lambda-list-keywords)
         finally (return
		      `(progn (declaim (ftype (function (&rest t) ,type) ,name))
			(defun ,name ,(nconc required-parameters rest)
			  (declare (optimize (speed 3)
					     (space 0)
					     (debug 3)
					     (safety 3)))
			  ,@(if declarations `((declare ,@declarations)))
			  ,@body)))))


This would let be write something like

(typed-defun double-float foo ((a double-float) (b double-float))
    (* a b))

> 
>>By the way is there a page explaining how this all works (I mean
>>lambda list parsing and keyword handling and so on) ?
> 
> 
> Look in section 3.4 of the Hyperspec.

Thanks, I'll surely take a look there.

Martin
From: Peter Seibel
Subject: Re: my-defun
Date: 
Message-ID: <m3vfgiemmy.fsf@javamonkey.com>
Raspaud Martin <········@free.fr> writes:

> Peter Seibel wrote:
>>>
>>>Now is it possible to enhance this so that the macro generates also
>>>(before the defun) something like
>>>(declaim (ftype (function (<types if specified, t otherwise>) a-type)
>>>name)))
>>>?
>> Sure. Probably the simplest way is to collect just the type names
>> into
>> a separate list in the "else" clause of the loop. (And you'll have to
>> figure out how to specify the return type of your function.
>>
>>>I've been trying but I'm getting stuck with the keywords again.
>> I'm not sure what you mean by that.
>
> Indeed I've been collecting as you said. However the loop macro stop
> whenever it finds a keyword. So how do I handle the rest of the
> parameters ? (like those that follow &key, &rest, &optional and so on)

Uh, don't stop. ;-) Maybe you missed the import of this line:

  until (member param lambda-list-keywords)

that's causes the loop to stop looping when it hits the first
&optional, &key, &rest, etc. If you also want information about those
you'll need to keep looping all the way through the lambda-list
collecting the various parts into the appropriate variables.

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Raspaud Martin
Subject: Re: my-defun
Date: 
Message-ID: <40fd3c64$0$8610$636a15ce@news.free.fr>
Peter Seibel wrote:
> Raspaud Martin <········@free.fr> writes:
>>
>>Indeed I've been collecting as you said. However the loop macro stop
>>whenever it finds a keyword. So how do I handle the rest of the
>>parameters ? (like those that follow &key, &rest, &optional and so on)
> 
> 
> Uh, don't stop. ;-) Maybe you missed the import of this line:
> 
>   until (member param lambda-list-keywords)
> 
> that's causes the loop to stop looping when it hits the first
> &optional, &key, &rest, etc. If you also want information about those
> you'll need to keep looping all the way through the lambda-list
> collecting the various parts into the appropriate variables.
> 

Ok but the information I need about the keyword part is only to "count" 
the number of args... I think it's not possible to specify the lambda 
list for this part of the list (I would get things as
"[...]&key ((a-value 1) fixnum)", which I don't think are doable (to 
many possibilities if we have optional default value and type))

So I tried
`(declaim (ftype (function ,(map 'list #'(lambda (x) (if (member x 
lambda-list-keywords) x t)) rest) ,type) ,name))

but I still get the error :
"Warning: Definition has keyword args, but previous declaration doesn't."

But I'll try it your way to see if I get better results.

Thanks for the help anyway

Martin
From: Raspaud Martin
Subject: Re: my-defun
Date: 
Message-ID: <40fd3eb1$0$8610$636a15ce@news.free.fr>
Raspaud Martin wrote:

> 
> So I tried
> `(declaim (ftype (function ,(map 'list #'(lambda (x) (if (member x 
> lambda-list-keywords) x t)) rest) ,type) ,name))

appended to the gathered types of the first part of the lambda list I 
forgot to mention. Sorry.

Martin
From: Raspaud Martin
Subject: Re: my-defun
Date: 
Message-ID: <40fd92ee$0$15272$636a15ce@news.free.fr>
Raspaud Martin wrote:
> Now is it possible to enhance this so that the macro generates also 
> (before the defun) something like
> (declaim (ftype (function (<types if specified, t otherwise>) a-type) 
> name)))
> ?

Sorry for answering myself, but in case this might help someone in the 
future I used the following :

(declaim (ftype (function * any-type) name)))

This is enough for me since I make all the declarations inside the 
function itself. So I suppose specifying the argument types in the ftype 
declamation is useless...

Martin