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

actually I don't wanna complicated workarounds to avoid simple errors .... :)

Something like  join @a,@b is always ever wrong, but it's legal perl syntax.

I'm wishing a possibility to restrict the input to for example "obvious" scalars. The way ref \"1" works is OK for me, because "1" obviously results in a scalar.

It's only frustrating that the prototype mechanism doesn't support any way to achieve this.

IMHO something like "accept every argument where ref argument results in XYZ" would facilitate a lot of error checking.

Cheers Rolf

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

    Checking the parameters with ref is good.

    Wherever it makes sense, I like to have the function detect that you've passed in an arrayref instead of a simple scalar, and then call itself foreach (@$param). Same with hashes. Lets you munge entire trees of values in one go, in a DWIM sort of way.

    Wherever that doesn't make sense, use croak and explain why the parameter is bad. You're not even limited to just "type". You can throw useful errors about much more subtle problems: "Temperature parameter cannot be less than absolute zero in call to set_thermostat() at monkey.pl line 42". Detailed and specific explanations of what went wrong are good.

    Don't limit yourself to prototypes, they rarely help. Just put in some good old preconditions with plain text explanations of what the caller did wrong.

      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!

        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 ''
        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!