in reply to Re^3: Prototype for constant items???
in thread Prototype for constant items???

I agree with you that run-time checking is the next good thing to do, but I think you missed the point:

I can't assure that I don't get erroneously the scalar of an array, without excluding constants!!!

Neither at compile-time nor at run-time.

Please try to give me a function that only accepts plain scalar values with "good old preconditions" and I'll be happy! ¹

BTW: I'm not limiting myself to prototypes, I'm just trying to widen my possibilities...

Cheers Rolf

(1) well maybe with some caller parsing it's somehow feasible...

UPDATE: I appended a practical use case with join to the startpost to make the problem less theoretical and easier understandable. Please try to give me a sub "newjoin" which croaks about this error!

Replies are listed 'Best First'.
Re^5: Prototype for constant items???
by SuicideJunkie (Vicar) on Sep 25, 2009 at 16:34 UTC

    The problem is, the scalar of an array IS just a plain old scalar. You can't expect to tell the difference between the size of an array (such as '3'), and a plain old number (such as '3'). And your function really shouldn't care how the caller came up with the parameter it passed you.

    If you expect an integer from 1 to 5, and you are passed the scalar of an array that happens to have between 1 and 5 elements, that is hardly wrong. It may just be a fancy way of counting widgets.

    use strict; use warnings; use Carp; use constant BAR => 3.14159265; my @list = ('p','i','e'); taster(BAR); taster(22/7); taster("constant"); taster(\@list); taster(@list); taster(scalar @list); sub taster { my $p1 = shift; carp "Hey, that's too many parameters!" if @_; print "Ref of $p1 is '".(ref $p1)."'\n"; }
    Gives
    Ref of 3.14159265 is '' Ref of 3.14285714285714 is '' Ref of constant is '' Ref of ARRAY(0x182af0c) is 'ARRAY' Hey, that's too many parameters! at test.pl line 18 main::taster('p', 'i', 'e') called at test.pl line 12 Ref of p is '' Ref of 3 is ''
      You can't expect to tell the difference between the size of an array (such as '3'), and a plain old number (such as '3').

      Again: I can tell the difference between scalar and an array, but just not for constants.

      You're still missing the point ... I marked it for you, to make it clearer:

      DB<4> sub tst (\[$@]) { print ref $_[0] } DB<5> $a="a";@a=(1..3) DB<6> tst $a SCALAR DB<7> tst @a ARRAY DB<8> tst "a" # <--------- that's the point ! Type of arg 1 to main::tst must be one of [$@] (not constant item) at +(eval 12)[/usr/share/perl/5.10/perl5db.pl:638] line 2, at EOF

      Cheers Rolf

        The problem there, is that you are still using prototypes. I am saying; ditch that, and just check the parameters in the sub.

        If the caller wants to pass an array as the first parameter, they can throw a reference slash in front of it, or square brackets around it. If they want to pass a list of multiple parameters, they can just pop the ($alpha,@rgb) in there without your sub forcing it into just two parameters and then trying to untangle it later.

        My main point: Use prototypes if you want to mangle the caller's code. Use if croak, warn, etc if you want to do type and sanity checking.

        Note: If they pass an array where they should have passed an arrayRef, then you should croak with a detailed version of "Too Many Parameters!" and/or "Value out of range", where applicable.

        Come to think of it, it would be nice to have a template of:

        sub foo { my $failed; ($failed = 1 and carp "First Parameter must be greater than zero") i +f $_[0] <= 0; ($failed = 1 and carp "Too many parameters") if scalar @_ > 3; die "Invalid parameters; cannot continue" if $failed; ... }
        since that would print out all of the errors before stopping, rather than just one error per run.

Re^5: Prototype for constant items???
by Porculus (Hermit) on Sep 26, 2009 at 17:37 UTC
    Please try to give me a sub "newjoin" which croaks about this error!

    It is, unfortunately, impossible.

    You can get pretty close with some runtime heuristics. It's unlikely that people will often deliberately try to join a single-element list using a number as the delimiter, so:

    sub newjoin ($@) { my $delim = shift; if (@_ == 1 and $delim =~ /^\d+$/) { carp "Did you mean to put those parameters the other way round +?"; } return join $delim, @_; }

    Whether that will extend to other functions is, of course, highly dependent on their specific signatures.

    I agree with you that it would be nice to have a robust general solution here, but it would seem to be quite a complicated thing to arrange for what could be fairly small gains in practice, and then you have to consider the risk of breaking existing code if you go round messing with how fundamental builtins like join() parse their arguments!

      Hi Porculus,

      I think you know that join was just an example to illustrate why I'm asking for an extended prototype mechanism. Confusion between scalar and list syntax are one of the main traps in perl.

      I don't intend to break existing code nor to change CORE::join. I just want to discuss how to widen the possibilities of future code. And this without waiting another decade for the new perl6 "messiah"¹.

      It can't be to difficult to extend the perl5 parser to fill this gap in the prototype definitions. Actually the drawbacks of prototypes are so big that everybody is discouraged to use them.

      (Anyway after further meditation I think it was a major design error not to map the scalar of an array on it's ref instead of it's length. An unintentional ref can be much easier spotted at runtime. )

      BTW: Your example code won't work with swaped parameters: join @list,$delim since the join of a 1-element list has no delimiter.

      Cheers Rolf

      (1) don't get me wrong I like perl6, but significantly delaying the evolution of perl5 in consequence is close to osborning.