leocharre has asked for the wisdom of the Perl Monks concerning the following question:

So.. I see this every here and there scattered throughout some subs in my code..

sub resolve_value { my $start_with = another_value() or warn("Could not get another value") and return; # continue.. }

I want to have a function/subroutine that will cause the caller to return something (null in this case), and whatever arguments(s) it may have, are output to STDERR, as warn would.

So the same thing rewritten would be as..

sub resolve_value { my $start_with = another_value() or wreturn("Could not get another value"); # continue.. }
(Setting aside how useless and waste of time these kind of "ideas" are..)
Where would I start to look for reading about- how to do this? (This is not necessarily messing with perl internals, is it? That's a little intimidating.)

It's a little bit weird because such a sub/command/keyword/function would not simply return something or not, but would cause the calling code/sub to return, itself.

Replies are listed 'Best First'.
Re: how to write a modified return() call?
by Corion (Patriarch) on Jul 14, 2009 at 14:02 UTC

    I wouldn't do it your intended way but do

    my $start_with = another_value() or return mywarn("Could not get another value");
    with
    sub mywarn { warn $_ for @_; return; };

    but if you're hell-bent on confusing people with your program flow, see Scope::Upper and/or Continuation::Escape, which allow you to "skip" levels as you return up the call chain.

      You know, that just pointed out the obvious to me. If I had this working properly.. It's not really perl anymore, is it.

      We can look at somebody else's code, a subroutine, and we can have a good idea of where something returns. It seems the only safe way to do this would be to very explicitly name the function, something like warn_and_return_undef()- which kinda would beat the purpose of brevity- much better to actually warn and return undef.

      I think this is what Fred P Brooks in the mythical man month (?) talks about in relation to sharpening tools that sharpen tools. A vicious programmer vice.

Re: how to write a modified return() call?
by ikegami (Patriarch) on Jul 14, 2009 at 14:42 UTC
    The following would be more readable.
    my $start_with = another_value() or ( warn("Could not get another value"), return );
    It hides the return at the end of the line, so you could break the lines.
    my $start_with = another_value() or ( warn("Could not get another value"), return );
      I don't find that very readable. It makes me wonder whether the parens are a list, or whether they are just there for precedence. And it makes me wonder if perl couldn't evaluate the return before the warn.

      I would write it as:

      my $start_with = another_value or do { warn "Could not get another value"; return; };
      Alternatively,
      do { warn "Could not get another value"; return; } unless my $start_with = another_value;
      but that focusses on the exception, not on the action, so I'll be far less likely to write it that way.
        wow! I can't imagine why you're so afraid of using ",". You need to use it for just about every function call!

      Thank you, this is useful!

      Two questions.. One on topic and one off topic.

      1. You say..
        my $start_with = another_value() or ( warn("Could not get another value"), return );
        Why not..
        my $start_with = another_value() or warn("Could not get another value"), return;
        Or even
        my $start_with = another_value() or warn "Could not get another value", return;
        I tried these out and they seem to work fine. I suppose it's maybe trying to mess with the compiler/interpreter being vague with arguments to warn(), in the last example.

        Are you using parenthesis as good practice/legibility only?

      2. It seems that unless I want to create my own perl distro, there's no way to do as JavaFan suggests ( 779943 ).

        Perl's pretty freaking liberal with what it lets you do with symbol tables, but.. we can't just add another function to the language at compile time.

        I wonder if this is something lisp could do- probably not- this must be on the language design (compiler/interpreter) level, not the coding level (?).
        ( Actually, as Corion noted initially.. 779928 )

        Why not..

        I didn't check the relative precedence of "or" and ",". I also liked the visual grouping.

        Or even

        No, it doesn't work. It returns before evaluating warn.

        It seems that unless I want to create my own perl distro, there's no way to do as JavaFan suggests

        What JavaFan suggested works as is. If you mean to make it pretty... The best way probably would require language-level support.

        we can't just add another function to the language at compile time

        I guess it depends on what you mean by function. Out of context, I'd say that's not true.

Re: how to write a modified return() call?
by moritz (Cardinal) on Jul 14, 2009 at 14:54 UTC
    That might be a bit overkill for your situation, but you could solve your problem with continuation passing, something along these lines:
    sub resolve_value { my $start_with = another_value(); unless $start_with { warn "Could not get another value"; return; } my $continuation = shift; return $continuation->($start_with, @_); } # and here's how you use that code: sub foo { # do some work here resolve_value(sub { my $start_with = shift; # do the rest of the work here }) }

    If you care about performance you could also tail-call into the sub:

    goto &resolve_value(sub { ... });
      I think you've been spending too much time in Rakudo moritz ;)

      unless $start_with { ... } # missing "()" around $start_with


      Smoothie, smoothie, hundre prosent naturlig!
Re: how to write a modified return() call?
by JavaFan (Canon) on Jul 14, 2009 at 14:56 UTC
    As far as I know, there isn't a pure Perl way of returning from *two* stack frames - that is, bypassing the wishes of your calling code. However, you may be able to write something like (untested):
    my $start_with = another_value() or do { @_ = ("Could not get another value"); goto &wreturn; }; sub wreturn {warn @_}
    But I don't think that really solves the issues you have.
Re: how to write a modified return() call?
by Anonymous Monk on Jul 15, 2009 at 08:51 UTC
    See Scope::Upper and it's unwind function - it allows you returning up in the call stack. You'd need to return to smth. like UP SUB UP SUB.