in reply to Re: Thoughts on new 'class' OO in upcoming perl
in thread Thoughts on new 'class' OO in upcoming perl

For what it's worth, I believe this will work, even without support for :common:

class Foo { field $x :param = 0; field $y :param = 0; } sub Foo::create ( $class, %args ) { if ( $args{x} < 0 ) { return undef; } else { return $class->new( %args ); } } my $foo = Foo->create( x => $x, y => $y ) or do_some_error_handling();

Not the nicest workaround, but also not terrible.

Replies are listed 'Best First'.
Re^3: Thoughts on new 'class' OO in upcoming perl
by cavac (Prior) on Mar 06, 2023 at 14:56 UTC
        Technically correct, but it ignores one little detail: Calling ->new is the normal, default way to create an object. Calling bless directly is not.

        There's quite a difference between expecting people to not instantiate an object in a way which 99% of classes consider to be an unsupported backdoor "here be dragons" approach (bless) and expecting them to not instantiate it in a way which 99% of classes consider to be the standard way to do it (calling the ->new method).

      How would you prevent someone from calling new() directly, in this case skipping your argument checks?

      Actually, this is possible. Your valid constructor methods just create a sufficiently secure random token and pass this to new. Here's a simple example where you can create objects with calls to the valid_constructor function, but not by directly calling new:

      use 5.37.9; use feature 'class'; no warnings 'experimental'; class No::New { use Carp; field $token :param; my %valid_tokens; ADJUST { delete $valid_tokens{$token} or croak "Directly calling ", __PACKAGE__, "->new is forbidden"; } sub valid_constructor { my $token = rand; $valid_tokens{$token} = 1; __PACKAGE__->new(token => $token); } method text { "Congratulations!"; } } say No::New->valid_constructor->text;

      Such a technique, as already mentioned by tobyink, is entirely impossible for bless. Since all "traditional" Perl OO frameworks including Object::Pad are based on blessed references, only core OO can prevent objects like bless \q"Arbitrary junk", Your::Class from happening!

        A slightly neater way:

        use 5.37.9; no warnings qw( experimental ); use feature qw( class defer ); use builtins qw( true false ); class No::New { use Carp; my $allow_construction = false; ADJUST { croak "Directly calling @{[ __PACKAGE__ ]}->new is forbidden" unless $allow_construction; } sub create { my $class = shift; $allow_construction = true; defer { $allow_construction = false }; return $class->new; } method text { "Congratulations!"; } } say No::New->create->text;

        (Note: I haven't actually tried this as the most recent Perl I have installed is 5.37.2.)