Magic for object constructor wanted

Magic for object constructor wanted

am 31.01.2008 07:05:45 von koszalekopalek

My code creates new objects and then populates them with
data, like this:

my $joe = Dude->new();
my $tom = Dude->new();
my $ann = Dude->new();
my $jane = Dude->new();

$joe->fill(
name => "joe",
friends => [ $ann, $tom ]
);

$jane->fill(
name => "jane",
friends => [ $ann ]
);

You will notice that the name field is always the same as
the variable that stores the object. What kind of magic
would I have to use in the constructor (or in the fill()
method) so that the name of the object does not have
to be specified explicitly? In other words, I would like to
be able to say:

$joe->fill(
friends => [ $ann, tom ]
);

$jane->fill(
friends => [ $ann ]
);

and still have the name field set. This saves typing and
makes sure the variable name and the name field match.

Is this possible?

Koszalek

Re: Magic for object constructor wanted

am 31.01.2008 07:14:11 von koszalekopalek

On Jan 31, 7:05 am, Koszalek Opalek wrote:

(...)

> the name of the object does not have
> to be specified explicitly? In other words, I would like to
> be able to say:
>
> $joe->fill(
> friends => [ $ann, tom ]
> );
>
> $jane->fill(
> friends => [ $ann ]
> );
>
> and still have the name field set. This saves typing and
> makes sure the variable name and the name field match.


The following just occurred to me. It would be cool...
if it worked :-) I have created a @Dudes array and then
iterate over the array creating objects dynamically
with eval() and passing whatever I need to the constructor.

The code errors out with
Can't call method "friends" on an undefined value at q.pl line 35.

Is there a way to declare variables with eval() in the
calling scope?

#!/usr/bin/perl
use strict;
no strict "vars";
use warnings;


{
package Dude;

sub new
{
my $class = shift;
my $self = {@_};
print "new() executed\n";
bless $self, $class;
};

sub friends
{
print "hello from $_[0]->{name}\n";
}
}


my @Dudes = ("joe", "tim");

for (@Dudes) {
my $code = qq{my \$$_ = Dude->new( "name" => "$_"); \$joe-
>friends();};
print "$code\n";
eval $code;
};

$joe->friends();


__END__

Koszalek

Re: Magic for object constructor wanted

am 31.01.2008 08:39:32 von Martijn Lievaart

On Wed, 30 Jan 2008 22:05:45 -0800, Koszalek Opalek wrote:

> My code creates new objects and then populates them with data, like
> this:
>
> my $joe = Dude->new();
> my $tom = Dude->new();
> my $ann = Dude->new();
> my $jane = Dude->new();
>
> $joe->fill(
> name => "joe",
> friends => [ $ann, $tom ]
> );
>
> $jane->fill(
> name => "jane",
> friends => [ $ann ]
> );
>
> You will notice that the name field is always the same as the variable
> that stores the object. What kind of magic would I have to use in the
> constructor (or in the fill() method) so that the name of the object
> does not have to be specified explicitly? In other words, I would like
> to be able to say:
>
> $joe->fill(
> friends => [ $ann, tom ]
> );
>
> $jane->fill(
> friends => [ $ann ]
> );
>
> and still have the name field set. This saves typing and makes sure the
> variable name and the name field match.

Sure. Just don't put the objects in separate variables, put them in a
hash.


Even better, bless the hash, iow make it its own class.

Then you get something like:

use Friends;

$friends = new Friends;


$friends->add(
name => "joe",
friends => [ "ann", "tom" ]
);

$friends->add(
name => "jane",
friends => [ "ann" ]
);

Or, if these are the only options, even shorter, either:

$friends->add("joe",[ "ann", "tom" ]);

or even:

$friends->add("joe", "ann", "tom" );

The disadvantage is that you loose the compile time checking wether those
friends exist. You code will not compile if I use $anne instead of $ann,
my suggestion will compile, run and probably not even notice.

What you also can do, is store the name of the Dude in the Dude object.
This is better design anyhow, but does not save you the extra redundancy
of specifying the name twice:

my $joe = Dude->new("joe");
my $tom = Dude->new("tom");
my $ann = Dude->new("ann");
my $jane = Dude->new("jane");

$joe->add_friends($ann, $tom);
$jane->add_friends($ann);


M4

Re: Magic for object constructor wanted

am 31.01.2008 13:49:44 von koszalekopalek

On Jan 31, 8:39 am, Martijn Lievaart wrote:

> Sure. Just don't put the objects in separate variables, put them in a
> hash.
>
> Even better, bless the hash, iow make it its own class.
>
> Then you get something like:
>
> use Friends;

I'm afraid that misses the point (or I have misunderstood
you). I do not want to bless my objects into Friends.
I want to bless each one into a signle Dude so that I
can do things like

$joe->whoami()

(And I do not want to rewrite code, that takes it for
granted that $joe, $tim, and $ann have all been blessed
into Dude's.)


> What you also can do, is store the name of the Dude in the Dude object.
> This is better design anyhow, but does not save you the extra redundancy
> of specifying the name twice:
>
> my $joe = Dude->new("joe");
> my $tom = Dude->new("tom");
> my $ann = Dude->new("ann");
> my $jane = Dude->new("jane");


That is not different from what I originally
posted except that (name=>"joe") has been
moved from fill() to new().

Koszalek

Re: Magic for object constructor wanted

am 31.01.2008 15:18:13 von bugbear

Koszalek Opalek wrote:
> My code creates new objects and then populates them with
> data, like this:
>
> my $joe = Dude->new();
> my $tom = Dude->new();
> my $ann = Dude->new();
> my $jane = Dude->new();

I suggest that it's probably better to work like this:

my @people = (
Dude->new("joe"),
Dude->new("tom"),
Dude->new("ann").
Dude->new("jane")
);

Alternatively/additioanaly, you might have a "group"
class (set of Dudes) that allow Dudes t be found by name.

If your set of names becomes large, declaring all the
variables implied by your original approach becomes ugly.

BugBear

Re: Magic for object constructor wanted

am 31.01.2008 16:58:27 von Peter Scott

On Wed, 30 Jan 2008 22:05:45 -0800, Koszalek Opalek wrote:
> My code creates new objects and then populates them with
> data, like this:
>
> my $joe = Dude->new();
> my $tom = Dude->new();
> my $ann = Dude->new();
> my $jane = Dude->new();
>
> $joe->fill(
> name => "joe",
> friends => [ $ann, $tom ]
> );
>
> $jane->fill(
> name => "jane",
> friends => [ $ann ]
> );
>
> You will notice that the name field is always the same as
> the variable that stores the object. What kind of magic
> would I have to use in the constructor (or in the fill()
> method) so that the name of the object does not have
> to be specified explicitly? In other words, I would like to
> be able to say:
>
> $joe->fill(
> friends => [ $ann, tom ]
> );
>
> $jane->fill(
> friends => [ $ann ]
> );
>
> and still have the name field set. This saves typing and
> makes sure the variable name and the name field match.
>
> Is this possible?

Possible, yes. Ugly, too. What you want is a sure-fire sign of a bad
design. I'm only going to show how to do it as an academic exercise to
demonstrate that Perl makes even bad ideas possible. I'm not going to
answer any questions about it. I think it would be a hideous mistake to
put it in production code.

$ cat /tmp/foo
#!/usr/local/bin/perl
use strict;
use warnings;

use PadWalker qw(peek_my);
use List::Util qw(first);

my $my_name = 42;

get_name(\$my_name);

sub get_name {
my $ref = shift;

my $h = peek_my(1);
my $name = first { ref $ref && $h->{$_} eq $ref } keys %$h;
print "Object variable name = ", ($name || ""), "\n";
}

$ /tmp/foo
Object variable name = $my_name

--
Peter Scott
http://www.perlmedic.com/
http://www.perldebugged.com/

Re: Magic for object constructor wanted

am 31.01.2008 17:56:33 von Michele Dondi

On Wed, 30 Jan 2008 22:05:45 -0800 (PST), Koszalek Opalek
wrote:

>My code creates new objects and then populates them with
>data, like this:
>
>my $joe = Dude->new();
>my $tom = Dude->new();
>my $ann = Dude->new();
>my $jane = Dude->new();
>
>$joe->fill(
> name => "joe",
> friends => [ $ann, $tom ]
>);
>
>$jane->fill(
> name => "jane",
> friends => [ $ann ]
>);
>
>You will notice that the name field is always the same as
>the variable that stores the object. What kind of magic
>would I have to use in the constructor (or in the fill()
>method) so that the name of the object does not have
>to be specified explicitly? In other words, I would like to
>be able to say:
>
>$joe->fill(
> friends => [ $ann, tom ]
>);
>
>$jane->fill(
> friends => [ $ann ]
>);
>
>and still have the name field set. This saves typing and
>makes sure the variable name and the name field match.
>
>Is this possible?

It *may* be possible with some XS (or PadWalker, but I'm not sure) but
it's horrible, anyway. Actually, your problem is *similar* albeit not
exactly the same, as the variable-in-variable-name one: in that case
one is generally told that symrefs are strictly speaking the solution,
but to use a hash instead. And that seems the viable solution here
too: you have a set of duded identified by their name. Thus, you don't
want the identification bit to be a variable name, since a hash index
is more appropriate. There are tons of ways to do so, but one that
springs to mind is to make new() accept a name, which is reasonable.
Thus

my %dude = map { $_ => Dude->new( name => $_ ) }
qw/joe tom ann jane/;

Then

$dude{joe}->fill( friends => [map $dude{$_}, qw/ann tom/] );
# etc.

Of course you may factor hide all the details behind convenient helper
subs.


Michele
--
{$_=pack'B8'x25,unpack'A8'x32,$a^=sub{pop^pop}->(map substr
(($a||=join'',map--$|x$_,(unpack'w',unpack'u','G^ ..'KYU;*EVH[.FHF2W+#"\Z*5TI/ER 256),7,249);s/[^\w,]/ /g;$ \=/^J/?$/:"\r";print,redo}#JAPH,

Re: Magic for object constructor wanted

am 31.01.2008 18:06:23 von koszalekopalek

On Jan 31, 3:18=A0pm, bugbear wrote:
> Koszalek Opalek wrote:
>
> my @people =3D (
> =A0 =A0 =A0Dude->new("joe"),
> =A0 =A0 =A0Dude->new("tom"),
> =A0 =A0 =A0Dude->new("ann").
> =A0 =A0 =A0Dude->new("jane")
> );
>
> Alternatively/additioanaly, you might have a "group"
> class (set of Dudes) that allow Dudes t be found by name.

No, then instead of saying
$ann->fill( friends =3D> [ $tom, $tim] );
I'd have to say smth like
$ann->fill( friends =3D> Friends->findByName("tom", "tim") );


> If your set of names becomes large, declaring all the
> variables implied by your original approach becomes ugly.

It will never get very large.

You might be thinking Dudes are some jerks from a database.
In fact they are tokens. The module user has two define
Dudes (tokens) and relationships between them and the
module will produce a parse tree. (Specifying Dudes
using a BNF grammar is not scheduled for this release:-)

Koszalek

Re: Magic for object constructor wanted

am 31.01.2008 23:19:59 von Martijn Lievaart

On Thu, 31 Jan 2008 04:49:44 -0800, Koszalek Opalek wrote:

> On Jan 31, 8:39 am, Martijn Lievaart wrote:
>
>> Sure. Just don't put the objects in separate variables, put them in a
>> hash.
>>
>> Even better, bless the hash, iow make it its own class.
>>
>> Then you get something like:
>>
>> use Friends;
>
> I'm afraid that misses the point (or I have misunderstood you). I do
> not want to bless my objects into Friends. I want to bless each one into
> a signle Dude so that I can do things like
>
> $joe->whoami()

$friends->{"joe"}->whoami();

I think you misunderstood me.

>
> (And I do not want to rewrite code, that takes it for granted that $joe,
> $tim, and $ann have all been blessed into Dude's.)

That's different. That's a requirement that's hard to meet with your
other wish.

>> What you also can do, is store the name of the Dude in the Dude object.
>> This is better design anyhow, but does not save you the extra
>> redundancy of specifying the name twice:
>>
>> my $joe = Dude->new("joe");
>> my $tom = Dude->new("tom");
>> my $ann = Dude->new("ann");
>> my $jane = Dude->new("jane");
>
>
> That is not different from what I originally posted except that
> (name=>"joe") has been moved from fill() to new().

It's better design, that's what different. A Dude normally does not get a
name once he gets friends, he has a name, so the constructor should set
the name.

M4

Re: Magic for object constructor wanted

am 01.02.2008 11:29:11 von rvtol+news

Martijn Lievaart schreef:

> A Dude normally does not
> get a name once he gets friends, he has a name, so the constructor
> should set the name.

Best let the constructor only construct, and make a separate initiator
to initialize values.

--
Affijn, Ruud

"Gewoon is een tijger."

Re: Magic for object constructor wanted

am 01.02.2008 14:13:35 von bugbear

Koszalek Opalek wrote:
> On Jan 31, 3:18 pm, bugbear wrote:
>> Koszalek Opalek wrote:
>>
>> my @people = (
>> Dude->new("joe"),
>> Dude->new("tom"),
>> Dude->new("ann").
>> Dude->new("jane")
>> );
>>
>> Alternatively/additioanaly, you might have a "group"
>> class (set of Dudes) that allow Dudes t be found by name.
>
> No, then instead of saying
> $ann->fill( friends => [ $tom, $tim] );
> I'd have to say smth like
> $ann->fill( friends => Friends->findByName("tom", "tim") );

that would depend on the two classes (Dude, group)

I see no reason:

$ann->fill("tom", "tim") cannot be made to work.

BugBear

Re: Magic for object constructor wanted

am 01.02.2008 23:18:04 von Martijn Lievaart

On Fri, 01 Feb 2008 11:29:11 +0100, Dr.Ruud wrote:

> Martijn Lievaart schreef:
>
>> A Dude normally does not
>> get a name once he gets friends, he has a name, so the constructor
>> should set the name.
>
> Best let the constructor only construct, and make a separate initiator
> to initialize values.

I beg to differ. An object should be in a determined state after each
method call (and a constructor is a method call). If an empty Dude makes
sense, no problem. But:

$joe = New Dude;
$joe->init("joe", other parameters);

makes no sense to me. The Dude is ment to be joe, an empty Dude object
makes no sense here.

M4