Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Moose: return null object

by halfcountplus (Hermit)
on Apr 18, 2011 at 15:22 UTC ( #899958=perlquestion: print w/replies, xml ) Need Help??
halfcountplus has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to be able to do this with a moose class:

my $eg = Whatever::Class->new(arg1=>'someval'); if (!$eg) [ move on ]
However, it does not seem to be possible. Point being, I want to submit args to the constructor, and if they are invalid or there is some other reason not to create the object, I want a null object returned.

I do not want to throw exceptions in the constructor that I have to catch via some Exception handling module.

I do not want to have to write routines external to the class that will check and see if I can construct an object before I try to construct the object.

I want all the arg handling and validation in the class definition, and I want a null object back if something doesn't fly. No fatalities, etc. Shouldn't this be simple?

Replies are listed 'Best First'.
Re: Moose: return null object
by Your Mother (Chancellor) on Apr 18, 2011 at 15:27 UTC

    Is there something wrong with–?

    my $eg = eval { Whatever::Class->new(arg1=>'someval') }; if (!$eg) [ move on ]

    As long as the class doesn't do its own exception catching and returns something for failure, it should be fine and do what you want.

      As long as the class...returns something for failure

      Well, obviously there is no choice about that, lol. No matter what, it is going to return a hash ref.

      I'd rather just set an "ok" Bool attribute in BUILD than use eval. It is not that I'm worried about invalid args (strings for ints, or whatever), it's to do with otherwise valid args in cases where the object cannot be created because of a program logic constraint.

      So all that's fine, just it seems (very) silly (as in: pointlessly silly, silly oversight kind of silly) that you cannot return undef from a "new" call, and thot perhaps I'd missed something. But I'm not here to pick on Moose either ;)

        So all that's fine, just it seems (very) silly (as in: pointlessly silly, silly oversight kind of silly) that you cannot return undef from a "new" call, and thot perhaps I'd missed something. But I'm not here to pick on Moose either ;)

        Well, basically it is not an oversight at all, it is *very* deliberate. This is basically a variant of the "return value" vs. "exception" debate which happens any time you lock a C programmer and Java programmer together in a room for a long enough period of time. We (myself and the other core Moose devs) believe that exceptions are the way to go, so therefore Moose reflects this opinion. But it also goes a little deeper then that, to the philosophy of what is a "Class".

        In non-Moose Perl OO, new was simply a method that you could make do whatever you wanted, convention said it was the method that constructed a new instance, but that was simply just convention. There was nothing special about new either, in fact it was a common idiom for a while to do $instance->new as a sort of cloning technique. But in the end, a "Class" in non-Moose Perl OO is not really a rigorously defined concept, but instead really just a loose set of conventions accumulated over time and borrowed from other languages. Now, there is nothing at all wrong with this, it is very powerful and extremely flexible, however it also suffers from the common "dark-side" of TIMTOWTDI.

        In contrast, Moose defines "Class" much more rigorously, in particular it thinks that one of the primary functions of a "Class" is as an instance construction factory. So much so that new in Moose will eventually call $class->meta->new_object to get the instance (delegating up to the Class meta-object). So keeping with this philosophy, new should *always* return an instance, and if it cannot for whatever reason return an instance, it should explode loudly with an exception.

Re: Moose: return null object
by bart (Canon) on Apr 18, 2011 at 15:27 UTC
    It's a pretty standard practice in Perl OO (not necessarily using Moose) to return undef from the sub new if you can't build an object. After all, news is just a plain sub, in Perl, unlike in a lot of other languages. The sample code you posted here then would just work.

    However, you might have a problem that it's not actually an object. Method calls and other attribute checking would all barf.

    So you might actually create a null object, by blessing it into some class and make sure that, when testing in boolean context, it returns false. I imagine you can use overload (with "bool") for that...

    In addition, you could have an AUTOLOAD sub which just does nothing, to avoid having to test if calling any method would croak.

Re: Moose: return null object
by ikegami (Pope) on Apr 18, 2011 at 15:32 UTC
    Are you saying you can't return undef from BUILDARGS? If so, then return undef from new.
    sub new { ... return undef; ... }

      Are you saying you can't return undef from BUILDARGS?

      That would be nice. Unfortunately, it's a fatal exception that requires catching:

      #!/usr/bin/perl -w use strict; package test; use Mouse; sub BUILDARGS { return undef } __PACKAGE__->meta->make_immutable(); my $eg = test->new(); print "All good!"; # nope...

      BUILDARGS did not return a HASH reference at ./ line 11.

      ..then return undef from new.

      Yer not suppose to use your own "new", but I did try defining one and having it return undef. No errors, however, the object returned is still a hash ref, not undef:

      package test; use Mouse; sub new { return undef } __PACKAGE__->meta->make_immutable(); my $eg = test->new(); print $eg;


        The title of this thread refers to Moose whereas your example uses Mouse. Moose behaves different in this example, so this might be considered to be a bug in Mouse?
        Not inlining a constructor for test since it defines its own construct +or. If you are certain you don't need to inline your constructor, specify +inline_constructor => 0 in your call to test->meta->make_immutable Use of uninitialized value $eg in print at line 14.

        Yer not suppose to use your own "new"

        That's an overstatement.

        No errors, however, the object returned is still a hash ref

        Sure, because you told Moose to use its own new instead of yours.

        package Module; use Mouse; sub new { return undef } __PACKAGE__->meta->make_immutable( inline_constructor => 0 ); print __PACKAGE__->new();

        I think it used to warn when new existed and you inlined the constructor. It really should. (Upd: OH! Moose does warn, it's Mouse that doesn't. Mouse-- lamprecht++ )

Re: Moose: return null object
by thargas (Deacon) on Apr 19, 2011 at 12:05 UTC

    Hmm. Seems to me that the purpose of new is to create an object. I would expect any object creation that failed would throw an exception, so I would think that the previously suggested:

    my $obj = eval { Class->new($whatever) }; if (not $obj) { do_something_else(); }
    would be the way to go. Obviously, there are different opinions, or this question would not have arisen.

    Another possibility would be to define another class method, say maybe_create(), which would wrap up an eval'd new(). That way you could have your optional object creation without violating the sanctity of the "new() returns an object or dies" idea.

      I too wanted to do this. being very new to Moose, I was a bit disappointed to find I could not easily have new return undef on failure. But there is a way. Use overload to have your object stringify to the empty string, or overload the bool operator so that it is 0, when its invalid. Then you can test it afterwards in the normal way. If you need to print an objects type use ref($x) in the normnal way. Some slightly dd code can result like my $x=Object::new(); $x or $x->printerror(); But you get used to that.
        Using constructor without the class parameter
        my $x = Object::new();

        instead of the canonical

        my $x = 'Object'->new;

        means it doesn't bless to its first parameter as usually

        sub new { bless {}, shift }

        which in turn means it doesn't work in child classes, because it always creates the object of the parent class.

        لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://899958]
Approved by ikegami
Front-paged by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (14)
As of 2016-10-27 15:01 GMT
Find Nodes?
    Voting Booth?
    How many different varieties (color, size, etc) of socks do you have in your sock drawer?

    Results (365 votes). Check out past polls.