construction clarification

0 views
Skip to first unread message

Carl Franks

unread,
May 30, 2005, 12:00:26 PM5/30/05
to perl6-language
I have a class that normally takes a list of named arguments.
I also want to be able to handle a single argument.

class Foo {
multi method new (Class $class: Str $date) {
return $class.bless(date => $date);
}

submethod BUILD ($.date, $.time, $.offset) {
# some error checking here
}
}

my $foo = Foo.new('2005-05-30');

#
Is this the correct way to handle my scenario?
My explicit 'new' method should only handle the case of
a single argument - otherwise the 'new' method
inherited from Class will handle a list of named
arguments.

My explicit BUILD submethod is called, regardless
of which 'new' method is called, correct?

Cheers,
Carl

Damian Conway

unread,
May 31, 2005, 8:29:48 PM5/31/05
to Carl Franks, perl6-language
Carl Franks wrote:

> I have a class that normally takes a list of named arguments.
> I also want to be able to handle a single argument.
>
> class Foo {
> multi method new (Class $class: Str $date) {
> return $class.bless(date => $date);
> }
>
> submethod BUILD ($.date, $.time, $.offset) {
> # some error checking here
> }
> }
>
> my $foo = Foo.new('2005-05-30');
>
> #
> Is this the correct way to handle my scenario?

It's *a* correct way. But redundant in this particular case.
The universal new() would handle the one-argument call exactly the same
as your overloaded new() does. Presumably, however, the one-argument variant
would do something else as well.


> My explicit 'new' method should only handle the case of
> a single argument - otherwise the 'new' method
> inherited from Class will handle a list of named
> arguments.

Correct.

> My explicit BUILD submethod is called, regardless
> of which 'new' method is called, correct?

Yes. It's invoked by bless().

Damian

Larry Wall

unread,
May 31, 2005, 7:32:59 PM5/31/05
to perl6-language
On Mon, May 30, 2005 at 05:00:26PM +0100, Carl Franks wrote:
: I have a class that normally takes a list of named arguments.

: I also want to be able to handle a single argument.
:
: class Foo {
: multi method new (Class $class: Str $date) {
: return $class.bless(date => $date);
: }
:
: submethod BUILD ($.date, $.time, $.offset) {
: # some error checking here
: }
: }
:
: my $foo = Foo.new('2005-05-30');
:
: #
: Is this the correct way to handle my scenario?

I think you probably want a "multi sub" there. In the current plan,
"multi method" is an MMD dispatch that occurs *after* dispatch to
the class. (Though it's possible we changed that and I've just
forgotten...)

: My explicit 'new' method should only handle the case of

: a single argument - otherwise the 'new' method
: inherited from Class will handle a list of named
: arguments.

Saying

multi sub new (Class $class, Str $date) {...}

should have that effect, provided you call it as new(Foo,'2005-05-30').

However, when called as Foo.new('2005-05-30'), all the multi subs
defined in the class hierarchy add themselves to the dispatch list
as if they were ordinary single-invocant methods, with additional
invocants treated as tie-breakers in the case that there are
multiple multi-subs in a single class. Or to put it another way,
all class-based instances of "multi sub" behave like "multi method"
when invoked via SMD. It dispatches first to the class, then looks
to see if there are multiple methods of that name within the class.
This is probably not what you want in this case, unfortunately.
We have specified that there's some kind of pragmatic way to
turn Foo.new() into MMD from the get-go, but that seems a bit crude.

So maybe that kind of dispatch needs to be reformulated a bit.
Perhaps Foo.new() should be forced to ordinary MMD any time any parent
class defines "multi sub new", so that all invocants are considered.
Or maybe Foo.new() is still not quite "ordinary MMD" in that it only
considers that subset of multis defined in Foo or parent classes of
Foo. If that dispatch fails it retries under ordinary MMD, including
methods defined outside of the classes in question. (Or another way to
say that might be that such non-class multis are always ordered after
all the class multis when invoked as Foo.new('2005-05-30'), unlike with
new(Foo,'2005-05-30'), which pays no attention to class boundaries.)

: My explicit BUILD submethod is called, regardless

: of which 'new' method is called, correct?

Yes, as long as the constructor in question calls the .bless built-in,
which always BUILDALL. (Though you could, of course, also redefine
BUILDALL evilly to bypass calling the correct BUILDs. But don't
do that.)

Larry

Carl Franks

unread,
Jun 1, 2005, 4:35:38 AM6/1/05
to Damian Conway, perl6-language
> It's *a* correct way. But redundant in this particular case.
> The universal new() would handle the one-argument call exactly the same
> as your overloaded new() does. Presumably, however, the one-argument variant
> would do something else as well.

Some people will need to call the constructor with a whole host of options:

my $foo = Foo.new(
date => '2005-06-01',
other => 1,
params => 1);

For the typical case though, rather than forcing people to have to write
my $foo = Foo.new(
date => '2005-06-01);

they should be able to write
my $foo = Foo.new('2005-06-01');

However, if I allowed the default 'new' to handle that case, then the
BUILD submethod has to be aware of that.
I thought it would be cleaner to 'document' the special case with a
seperate constructor, and also not require any special-case logic in the
BUILD submethod.

Is that really off the wall?

Carl

Carl Franks

unread,
Jun 1, 2005, 7:44:44 AM6/1/05
to Damian Conway, perl6-language
> The universal new() would handle the one-argument call exactly the
> same as your overloaded new() does.

Is that correct? S12 says...
All classes inherit a default new constructor from Object.
It expects all arguments to be named parameters initializing
attributes of the same name.
... which seems to contradict.

Carl

Damian Conway

unread,
Jun 1, 2005, 9:11:53 AM6/1/05
to perl6-l...@perl.org
Carl Franks wrote:
> However, if I allowed the default 'new' to handle that case, then the
> BUILD submethod has to be aware of that.
> I thought it would be cleaner to 'document' the special case with a
> seperate constructor, and also not require any special-case logic in the
> BUILD submethod.
>
> Is that really off the wall?

Not at all. It's entirely reasonable. As I mentioned in my previous message, I
simply missed your original intent.

Damian

Damian Conway

unread,
Jun 1, 2005, 9:10:26 AM6/1/05
to perl6-l...@perl.org
Carl Franks wrote:

Apologies. I hadn't noticed that you were intending to call it with a
positional argument. In which case, yes, you would indeed require the separate
constructor definition.

Damian

Reply all
Reply to author
Forward
0 new messages