[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

I wish CLOS generic functions provided a better optional argument default mechanism

Jim Newton

10/14/2015 8:36:00 AM

It occurs from time to time that several methods of the same generic function need an
optional argument which has the same default on all the methods. There's no way to
enforce this declaratively, as far as I know.

Here is how I wish it worked.

I'd like to be able to define default values on the optional argument declarations in the generic function. Then if any method does not provide an default value for its optional argument, the value should come from the generic function. Here's an example.

(defgeneric F (x &key (x 100) (y 200)))

(defmethod F ((x A) &key (x 1) (y 2))
...)

(defmethod F ((x B) &key (x nil) y) ; x defaults to nil, but y defaults to 200 as per the defgeneric
...)

(defmethod F ((x C) &key x y) ; x and y both default from the defgeneric, 100,100 respectively
...)

Of course it is normally easy to workaround this, such as declare global variables storing
the defaults, and explicitly provide that global (or lexical) variable as the default value in each
method definition.

E.g.,

(let ((x-default 100) (y-default 200))
(defgeneric F (x &key x y))

(defmethod F ((x A) &key (x 1) (y 2))
...)

(defmethod F ((x B) &key (x nil) (y y-default))
...)
(defmethod F ((x C) &key (x x-default) (y y-default))
..)
....)
7 Answers

Didier Verna

10/14/2015 9:10:00 AM

0

Jim Newton wrote:

> It occurs from time to time that several methods of the same generic
> function need an optional argument which has the same default on all
> the methods. There's no way to enforce this declaratively, as far as
> I know.
>
> Here is how I wish it worked.
>
> (defgeneric F (x &key (x 100) (y 200)))

Something can probably be hacked by subclassing
STANDARD-GENERIC-FUNCTION but you would need to rewrite DEFGENERIC to
allow this syntax.

If you want to continue using standard DEFGENERIC, it may be easier to
come up with something like this:

(defgeneric F (x &key y z)
(:generic-function-class 'your-class)
(:default-initargs :y 100 :z 200))

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...

Mirko

10/14/2015 2:10:00 PM

0

On Tuesday, October 13, 2015 at 11:09:21 PM UTC-10, Didier Verna wrote:
> Jim Newton wrote:
>
> > It occurs from time to time that several methods of the same generic
> > function need an optional argument which has the same default on all
> > the methods. There's no way to enforce this declaratively, as far as
> > I know.
> >
> > Here is how I wish it worked.
> >
> > (defgeneric F (x &key (x 100) (y 200)))
>
> Something can probably be hacked by subclassing
> STANDARD-GENERIC-FUNCTION but you would need to rewrite DEFGENERIC to
> allow this syntax.
>
> If you want to continue using standard DEFGENERIC, it may be easier to
> come up with something like this:
>
> (defgeneric F (x &key y z)
> (:generic-function-class 'your-class)
> (:default-initargs :y 100 :z 200))
>
> --
> My new Jazz CD entitled "Roots and Leaves" is out!
> Check it out: http://didierverna.com/records/roots-and-...
>
> Lisp, Jazz, Aïkido: http://www.didier...

How about a :before method checking the arguments and setting them?

Untested.

Mirko

Didier Verna

10/14/2015 3:39:00 PM

0

Mirko Vukovic <mirko.vukovic@gmail.com> wrote:

> How about a :before method checking the arguments and setting them?

That wouldn't work, because if you want to tweak the argument list,
you would need to (call-next-method with the new args) which is only
available in primary or around methods (at least in the standard
method combination).

On top of that, I think it's a bad idea because it would also
interfere with a user wanting to provide its own before/after/around
methods.

No, the only decent place to tweak the argument list is before it is
used at all.

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...

Kaz Kylheku

10/14/2015 6:45:00 PM

0

On 2015-10-14, Jim Newton <jimka.issy@gmail.com> wrote:
> I'd like to be able to define default values on the optional argument
> declarations in the generic function. Then if any method does not provide an
> default value for its optional argument, the value should come from the
> generic function. Here's an example.
>
> (defgeneric F (x &key (x 100) (y 200)))
>
> (defmethod F ((x A) &key (x 1) (y 2))
> ...)
>
> (defmethod F ((x B) &key (x nil) y) ; x defaults to nil, but y defaults to 200 as per the defgeneric
> ...)
>
> (defmethod F ((x C) &key x y) ; x and y both default from the defgeneric, 100,100 respectively
> ...)
>
> Of course it is normally easy to workaround this, such as declare global
> variables storing the defaults, and explicitly provide that global (or
> lexical) variable as the default value in each method definition.

That workaround is not satisfactory.

You should think about how these x and y quantities are related to the
classes.

Perhaps they need to be :allocation :class slots!

Int the code below, we have an ad-nauseum repeititon of the same &key
part of the lambda list in all the methods.

The default values are then controlled via slots in the classes.
Inheritance lets us override the defaults in a base class with
different values in the derived class.

The class framework has two important key parameters, "pparam" and "qparam"
which are used in the "foo" method. The foo method obtains the pparam
value based on the type of the left object, and the qparam based on the
right object.


(defclass base ()
((pparam :initform 42 :allocation :class :accessor pparam)
(qparam :initform 's :allocation :class :accessor qparam)))


(defclass derived (base)
((pparam :initform 43 :allocation :class :accessor pparam)
(qparam :initform 't :allocation :class :accessor qparam)))


(defgeneric foo (obj1 obj2 &key p q))

(defmethod foo ((obj1 base) (obj2 base) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))

(defmethod foo ((obj1 base) (obj2 derived) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))

(defmethod foo ((obj1 derived) (obj2 base) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))

(defmethod foo ((obj1 derived) (obj2 derived) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))


REPL:


P from base, Q from derived:

[1]> (foo (make-instance 'base) (make-instance 'derived))
p: 42, q: T
NIL

Vice versa:

[2]> (foo (make-instance 'derived) (make-instance 'base))
p: 43, q: S
NIL

Both overriden via keyword arguments:

[3]> (foo (make-instance 'derived) (make-instance 'base) :p 'over :q 'ride)
p: OVER, q: RIDE
NIL

What is missing?

- The repeated &key list is cumbersome. Solution: perhaps tuck it behind a macro?
- Preparing the defaults via inheritance is cumbersome. Solution: macro. :)

Madhu

10/15/2015 1:21:00 AM

0


* Jim Newton <5769a826-7057-4465-ab1b-f74dafb57e96@googlegroups.com> :
Wrote on Wed, 14 Oct 2015 01:36:00 -0700 (PDT):

| It occurs from time to time that several methods of the same generic
| function need an optional argument which has the same default on all
| the methods. There's no way to enforce this declaratively, as far as
| I know.
|
| Here is how I wish it worked.
|
| I'd like to be able to define default values on the optional argument
| declarations in the generic function. Then if any method does not
| provide an default value for its optional argument, the value should
| come from the generic function. Here's an example.
|
| (defgeneric F (x &key (x 100) (y 200)))
|
| (defmethod F ((x A) &key (x 1) (y 2))
| ...)
|
| (defmethod F ((x B) &key (x nil) y) ; x defaults to nil, but y defaults to 200 as per the defgeneric
| ...)

I think what you are asking, SHOULD NOT be provided by CL because the
requested syntax violates the principles on which CL's lambda list
syntax is based. The variable is introduced as an KEY parameter, and
its binding should be apparent at the point where it is introduced.

The CL idiom for the requested behaviour would be (something like)

(defmethod F ((b B) &key (x nil) (y nil y-supplied-p)) ;[1]
(let ((y (if y-supplied-p y (complicated defaulting-behaviour b :y))))
.... ))

The complicated-defaulting-behaviour could just be something like
(slot-value b 'y-default) where y-default could be an allocation slot on
the base class of B. And it could be moved up to the lambda list (See
the macro suggestion in Kylhelku's followup)---Madhu

[1] without repeating variable names in the lambda lists:

Pascal Costanza

10/17/2015 11:03:00 AM

0

On 14/10/2015 10:36, Jim Newton wrote:
> It occurs from time to time that several methods of the same generic function need an
> optional argument which has the same default on all the methods. There's no way to
> enforce this declaratively, as far as I know.
>
> Here is how I wish it worked.
>
> I'd like to be able to define default values on the optional argument declarations in the generic function. Then if any method does not provide an default value for its optional argument, the value should come from the generic function. Here's an example.
>
> (defgeneric F (x &key (x 100) (y 200)))
>
> (defmethod F ((x A) &key (x 1) (y 2))
> ...)
>
> (defmethod F ((x B) &key (x nil) y) ; x defaults to nil, but y defaults to 200 as per the defgeneric
> ...)
>
> (defmethod F ((x C) &key x y) ; x and y both default from the defgeneric, 100,100 respectively
> ...)
>
> Of course it is normally easy to workaround this, such as declare global variables storing
> the defaults, and explicitly provide that global (or lexical) variable as the default value in each
> method definition.
>
> E.g.,
>
> (let ((x-default 100) (y-default 200))
> (defgeneric F (x &key x y))
>
> (defmethod F ((x A) &key (x 1) (y 2))
> ...)
>
> (defmethod F ((x B) &key (x nil) (y y-default))
> ...)
> (defmethod F ((x C) &key (x x-default) (y y-default))
> ..)
> ...)
>

A solution that typically works well is to provide a separate wrapper
function that handles optional and keyword parameters, and than invokes
the generic function.

Pascal

--
My website: http:/...
Common Lisp Document Repository: http://cdr.eu...
Closer to MOP & ContextL: http://common-lisp.net/proje...
The views expressed are my own, and not those of my employer.

Kaz Kylheku

10/17/2015 1:45:00 PM

0

On 2015-10-17, Pascal Costanza <pc@p-cos.net> wrote:
> On 14/10/2015 10:36, Jim Newton wrote:
>> Of course it is normally easy to workaround this, such as declare global
>> variables storing the defaults, and explicitly provide that global (or
>> lexical) variable as the default value in each method definition.
>>
>> E.g.,
>>
>> (let ((x-default 100) (y-default 200))
>> (defgeneric F (x &key x y))
>>
>> (defmethod F ((x A) &key (x 1) (y 2))
>> ...)
>>
>> (defmethod F ((x B) &key (x nil) (y y-default))
>> ...)
>> (defmethod F ((x C) &key (x x-default) (y y-default))
>> ..)
>> ...)
>>
>
> A solution that typically works well is to provide a separate wrapper
> function that handles optional and keyword parameters, and than invokes
> the generic function.

A naive application of that approach will not handle the two levels of
defaulting required here.

The methods have to know whether a parameter is being supplied by the user in
the GF call, in order to supply the second-level default.

For instance, the &key (x 1) case wants to use 1 if the call
doesn't pass an argument. If the GF wrapper adjusts a missing x to the default
value of 200, that information is lost.