From: Bob Follek
Subject: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C47A319.F912F8C7@verizon.net>
I want to guarantee that make-instance includes a given initarg. This
works:

(defclass requires-arg ()
  ((arg :initarg :arg)))

(defmethod initialize-instance :after ((ra requires-arg) &rest initargs
&key &allow-other-keys)
  (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
:arg")))

Is there a cleaner way? Thanks

From: Steven M. Haflich
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C47BA2C.803D3702@pacbell.net>
Bob Follek wrote:
> 
> I want to guarantee that make-instance includes a given initarg. This
> works:
> 
> (defclass requires-arg ()
>   ((arg :initarg :arg)))
> 
> (defmethod initialize-instance :after ((ra requires-arg) &rest initargs
> &key &allow-other-keys)
>   (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
> :arg")))
> 
> Is there a cleaner way? Thanks

You code does _not_ ensure that the initarg was present.  What it
does is ensure that initialization has bound the slot prior to the
point that this :after method is called in the combined instance
initialization protocol.  Even if you somehow arranged for this
method to be run at the very end of initialization, ensuring the
slot is bound is not the same as ensuring a particular initarg
was present.  The slot could have been bound by a method, or there
could be multiple initargs all of which initialize a slot.

The classic way to guarantee that an initarg is present (if that's
what you really want to ensure) is something like this:

(defmethod initialize-instance :after ((ra requires-initarg)
                                        &key (arg
                                              (error "You must supply...")))
  (declare (ignore arg))
   )
From: Arthur Lemmens
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C47E101.BA93930B@xs4all.nl>
"Steven M. Haflich" wrote:

> The classic way to guarantee that an initarg is present (if that's
> what you really want to ensure) is something like this:
> 
> (defmethod initialize-instance :after ((ra requires-initarg)
>                                         &key (arg
>                                               (error "You must supply...")))
>   (declare (ignore arg))
>    )

You can also do something like:

  (defclass requires-arg ()
    ((arg :initarg :arg 
          :initform (error "You must supply..."))))

or:

  (defclass requires-arg ()
    ((arg :initarg :arg))
    (:default-initargs :arg (error "You must supply...")))
From: Steven M. Haflich
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C480346.32FD2176@pacbell.net>
Arthur Lemmens wrote:
> 
> "Steven M. Haflich" wrote:
> 
> > The classic way to guarantee that an initarg is present (if that's
> > what you really want to ensure) is something like this:
> >
> > (defmethod initialize-instance :after ((ra requires-initarg)
> >                                         &key (arg
> >                                               (error "You must supply...")))
> >   (declare (ignore arg))
> >    )
> 
> You can also do something like:

But each of these three solutions has slightly different semantics.
Mine above ensures that the keyword was supplied by the call to
make-instance, which was the original request.  It is insensitive
whether or not some _other_ keyword, or :default-initargs, or code
in some method, may have initialized the slot.  It does allow
a defaulted initarg to satisfy the requirement.

Your solution immediately below is similar except that it is
satisfied if the the slot is initialized by _any_ initarg, or by
some method on a more-specific class.

>   (defclass requires-arg ()
>     ((arg :initarg :arg
>           :initform (error "You must supply..."))))

The next solution is sensitive to the specific initarg, ignoring
whether the slot may have been initialized by some other initarg or
by some method.  It is very similar to mine except for one very
big point!
 
>   (defclass requires-arg ()
>     ((arg :initarg :arg))
>     (:default-initargs :arg (error "You must supply...")))

Your solution (like Follek's original) requires that :arg be
associated with a slot as an initarg for that slot.  That wasn't
a requirement expressed by Folleck, although it was implied by
his solution.  But an initarg may be used by some init method and
have no direct association as an initarg for a slot.
From: Kenny Tilton
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C4840ED.BFBD1FB4@nyc.rr.com>
(defclass money ()
  ((fast :initarg :fast)))

(defmethod initialize-instance ((self money) &rest iargs &key (fast t
fast-supplied))
  (declare (ignore fast))
  (assert fast-supplied))

(make-instance 'money :fast :yes!)
(make-instance 'money)

two things, tho: if you have a default-initarg code somewhere in the
class hierarchy, that indeed comes in qua initarg and fast-supplied will
be t, so this won't work if you are trying to make sure the initarg came
in via /coding/ of the make-instance form. The good news being that when
I play games like this with a slot there is no sensible reason for
having a default initarg on that slot. (not shown, but an initform comes
in differently, so you can have an initform and still detect an
unsupplied initarg.

second, a scary gotcha. while confirming all the above I put in a
default initarg, tested, then eliminated the default-initarg clause
altogether to try the initform variation. but when I did the test the
deleted default-initarg still came thru!!!! some (brain-dead) Lisps
(such as ACL) leave the class with the defaul-initarg!!!! You have to
code an empty (:default-initargs)!!!!!

If my feelings about this (mis)-interpretation of the MOP are not
clear...

:)

kenny
clinisys



kenny
clinisys

Bob Follek wrote:
> 
> I want to guarantee that make-instance includes a given initarg. This
> works:
> 
> (defclass requires-arg ()
>   ((arg :initarg :arg)))
> 
> (defmethod initialize-instance :after ((ra requires-arg) &rest initargs
> &key &allow-other-keys)
>   (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
> :arg")))
> 
> Is there a cleaner way? Thanks

(defmethod initialize-instance :after ((ra requires-arg) &rest initargs
> &key &allow-other-keys)
>   (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
> :arg")))
From: Steven M. Haflich
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C4883C2.DC3B7D5C@franz.com>
Kenny Tilton wrote:
 
> (defclass money ()
>   ((fast :initarg :fast)))
> 
> (defmethod initialize-instance ((self money) &rest iargs &key (fast t
> fast-supplied))
>   (declare (ignore fast))
>   (assert fast-supplied))
> 
> (make-instance 'money :fast :yes!)
> (make-instance 'money)
> 
> two things, tho: if you have a default-initarg code somewhere in the
> class hierarchy, that indeed comes in qua initarg and fast-supplied will
> be t, so this won't work if you are trying to make sure the initarg came
> in via /coding/ of the make-instance form. The good news being that when
> I play games like this with a slot there is no sensible reason for
> having a default initarg on that slot. (not shown, but an initform comes
> in differently, so you can have an initform and still detect an
> unsupplied initarg.
> 
> second, a scary gotcha. while confirming all the above I put in a
> default initarg, tested, then eliminated the default-initarg clause
> altogether to try the initform variation. but when I did the test the
> deleted default-initarg still came thru!!!! some (brain-dead) Lisps

I think you inadvertently typed "brain-dead" here when you should have
used an adjective like "correct."  (:-)

> (such as ACL) leave the class with the defaul-initarg!!!! You have to
> code an empty (:default-initargs)!!!!!
> 
> If my feelings about this (mis)-interpretation of the MOP are not
> clear...
> 
> :)

Please explain why you think this is a misinterpretation of the MOP.
I would agree that the behavior is apparently unspecified by the ANS,
but the chain of logic in the MP seems clear enough:

First, direct-default-initargs is contained in a class metaobject
for which there exists the reader class-direct-default-initargs.
(It's behavior is slot-like, although there is no requirement that
it be implemented as a slot.)  The direct-default-initargs is
initialized by the :default-initargs argument to ensure-class and
ensure-class-using-class.  (The actual value is munged by the defclass
macro to turn all the default-initarg value forms into lexical closures
so they capture the lexical environment of the defclass form.)
ensure-class-using-class is explicit that when the class already exists,
it is reinitialized by calling reinitialize-instance with the
canonicalized arguments from the new defclass form.  Therefore if the
:default-initargs argument was _not_ present in the defclass form
(and hence not present in the ensure-class call) then
reinitialize-instance on the class metaobject will not change the
value of the existing attribute.

The following is from the section Initialization of Class Metaobjects:

  Unless there is a specific note to the contrary, then during
  reinitialization, if an initialization argument is not supplied,
  
    The :direct-default-initargs argument is a list of
    canonicalized default initialization arguments. 

    An error is signaled if this value is not a proper list, or
    if any element of the list is not a canonicalized default
    initialization argument. 

    If the class metaobject is being initialized, this argument
    defaults to the empty list.

Note the word "initialized" not "reinitialized" in the last
sentence.

The following is from the description of ensure-class-using-class:

  Otherwise, the class metaobject class is redefined by calling
  the reinitialize-instance generic function with class and the
  initialization arguments. The class argument is then returned.
From: Steven M. Haflich
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C488B77.7CC5E26@franz.com>
Whoops, I deleted aline in my citationfrom the MOP,
making the section hard to understand.  Here (!!!) corrected:

> The following is from the section Initialization of Class Metaobjects:
> 
>   Unless there is a specific note to the contrary, then during
>   reinitialization, if an initialization argument is not supplied,
!!! the previously stored value is left unchanged. 
> 
>     The :direct-default-initargs argument is a list of
>     canonicalized default initialization arguments.
> 
>     An error is signaled if this value is not a proper list, or
>     if any element of the list is not a canonicalized default
>     initialization argument.
> 
>     If the class metaobject is being initialized, this argument
>     defaults to the empty list.
> 
> Note the word "initialized" not "reinitialized" in the last
> sentence.
From: Frederic Brunel
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <lay9ivr5zo.fsf@buzz.in-fusio.com>
Bob Follek <········@verizon.net> writes:

> (defclass requires-arg ()
>   ((arg :initarg :arg)))
> 
> (defmethod initialize-instance :after ((ra requires-arg) &rest initargs
> &key &allow-other-keys)
>   (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
> :arg")))
> 
> Is there a cleaner way? Thanks

The make-instance function should be considered as a low-level
function. The common way to use it is to hide its call behind another
function with optional or required parameters such as (for your
example):

(defun make-requires-arg (arg)
  (make-instance 'requires-arg :arg arg))

Now you have a construction which forces the user to pass an 
argument.

-- 
Frederic Brunel
Software Engineer
In-Fusio, The Mobile Fun Connection
From: Kenny Tilton
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C485B24.CBD24DF0@nyc.rr.com>
Frederic Brunel wrote:

> The make-instance function should be considered as a low-level
> function. The common way to use it is to hide its call behind another
> function with optional or required parameters such as (for your
> example):
> 
> (defun make-requires-arg (arg)
>   (make-instance 'requires-arg :arg arg))
> 
> Now you have a construction which forces the user to pass an
> argument.

But what stops me from coding a naked make-instance and bypassing the
requirement?

kenny
clinisys
From: Kalle Olavi Niemitalo
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <iznelknpnot.fsf@stekt34.oulu.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> But what stops me from coding a naked make-instance and bypassing the
> requirement?

The documentation of the class, I suppose.
You could also grep for inadvertent MAKE-INSTANCE calls in your code.
From: Kenny Tilton
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C487917.30FF1992@nyc.rr.com>
Kalle Olavi Niemitalo wrote:
> 
> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> > But what stops me from coding a naked make-instance and bypassing the
> > requirement?
> 
> The documentation of the class, I suppose.

No, that will not do. The system I have in mind wants to ensure that
things get handled a certain way even if the conscientious,
rule-abiding, doc-reading, line-toeing programmer screws up. It happens.

> You could also grep for inadvertent MAKE-INSTANCE calls in your code.

:)

kenny
clinisys
From: Ingvar Mattsson
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <87elkn4bd9.fsf@gruk.tech.ensign.ftech.net>
Kenny Tilton <·······@nyc.rr.com> writes:

> Kalle Olavi Niemitalo wrote:
> > 
> > Kenny Tilton <·······@nyc.rr.com> writes:
> > 
> > > But what stops me from coding a naked make-instance and bypassing the
> > > requirement?
> > 
> > The documentation of the class, I suppose.
> 
> No, that will not do. The system I have in mind wants to ensure that
> things get handled a certain way even if the conscientious,
> rule-abiding, doc-reading, line-toeing programmer screws up. It happens.

Would a suitable :after method to INITIALIZE-INSTANCE do the trick?
(defmacro define-required-slots (class &rest slots)
 `(defmethod initialize-instance :around ((class ,class) &rest args)
    (let ((new-object (call-next-method)))
      (loop for slot in ',slots
          unless (slot-boundp new-object slot)
          do (error "Slot ~s must be bound in class ~s" slot ',class))
        new-object)))

This will probably do roughly what you want, I think.
Not *quite* what you asked for (any class that has a default-initarg
for a slot or an initform will not be caught, but the one reason I can
see for requiring an argument on instance-making is for things that
*must* exist, but where no sensible default can be given).

The only testing I've done is of the very minor sort.

//Ingvar
-- 
((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x)))
	Probably KMP
From: Daniel Barlow
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <87n0zbwl0v.fsf@noetbook.telent.net>
Frederic Brunel <··········@in-fusio.com> writes:

> The make-instance function should be considered as a low-level
> function. The common way to use it is to hide its call behind another
> function with optional or required parameters such as (for your
> example):

Really?  I far prefer using classes that work with a bare
make-instance, because I can subclass them without messing about with
change-class.  Multiple inheritance makes it even more fun: if you
have make-foo and make-bar, and I have (defclass baz (foo bar) ()),
how do I instantiate a baz?


-dan

-- 

  http://ww.telent.net/cliki/ - Link farm for free CL-on-Unix resources 
From: Kenny Tilton
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <3C487CAF.EA06D3E8@nyc.rr.com>
Daniel Barlow wrote:
> 
> Frederic Brunel <··········@in-fusio.com> writes:
> 
> > The make-instance function should be considered as a low-level
> > function. The common way to use it is to hide its call behind another
> > function with optional or required parameters such as (for your
> > example):
> 
> Really?  I far prefer using classes that work with a bare
> make-instance, because I can subclass them without messing about with
> change-class.  Multiple inheritance makes it even more fun: if you
> have make-foo and make-bar, and I have (defclass baz (foo bar) ()),
> how do I instantiate a baz?

Now that you mention it, that is exactly why I dumped an early make-xxx
scheme, I realized I had broken make-instance.

kenny
clinisys
From: Alain Picard
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <86wuyfw943.fsf@gondolin.local.net>
Daniel Barlow <···@telent.net> writes:

> Really?  I far prefer using classes that work with a bare
> make-instance, because I can subclass them without messing about with
> change-class.  Multiple inheritance makes it even more fun: if you
> have make-foo and make-bar, and I have (defclass baz (foo bar) ()),
> how do I instantiate a baz?

I think you often find that the classes foo bar will most naturally
tend towards being abstract, i.e. some-foo-mixin and some-blah-mixin,
which are probably not really meant to be instantiated on their
own.  Therefore they will probably not have their own make-some-blah-mixin
constructor.

The BAZ is probably the real object, so make-baz becomes logical.

At least, that's what seems to naturally arise out of most of my
designs.  I'm curious to see real world (i.e. not contrived for
this discussion) examples where this is a problem.


-- 
It would be difficult to construe        Larry Wall, in  article
this as a feature.			 <·····················@netlabs.com>
From: Robert Monfera
Subject: Re: Newbie: Ensuring a Required Argument for Constructor?
Date: 
Message-ID: <cP628.246$FS4.339581@news2.news.adelphia.net>
"Frederic Brunel" <··········@in-fusio.com> wrote in message
···················@buzz.in-fusio.com...
| Bob Follek <········@verizon.net> writes:
|
| > (defclass requires-arg ()
| >   ((arg :initarg :arg)))
| >
| > (defmethod initialize-instance :after ((ra requires-arg) &rest initargs
| > &key &allow-other-keys)
| >   (when (not (slot-boundp ra 'arg)) (error "You must pass a value for
| > :arg")))
| >
| > Is there a cleaner way? Thanks
|
| The make-instance function should be considered as a low-level
| function. The common way to use it is to hide its call behind another
| function with optional or required parameters such as (for your
| example):
|
| (defun make-requires-arg (arg)
|   (make-instance 'requires-arg :arg arg))

This is an opinion for which you don't give any support.

I disagree with the manual make-* approach because it goes against object
orientation.  By crippling classes, no wonder they need crutches like these
wrapper functions.  For example, a subclass will be left out in the dark in
this specific case, and maybe the subclassing is outside your module.  The
designers of CLOS did a great job with the protocol and it should not be
routinely hidden by innocent-looking sugarcoating.  Even a module API can be
kept object-oriented.

There are many other ways of requiring an argument.  You can set up a
validation protocol via shared-initialize to enforce slot type declarations
or use an initform that raises an error.

There _are_ drawbacks to sticking to make-instance et al, for example, Lisp
IDEs cannot do a :who-instantiates reference (let alone a
:who-reinitializes) while they support :who-calls.  It's just they usually
do not negate the benefits.

Robert