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

Hi

I'd like to construct a prototype which checks at compile time for a "real scalar value" instead of forcing scalar context.

Using (\$) excludes literals and there doesn't seem to be any alternative for constants to be included in ((\[$@%&*]))

(more details in docs for Prototypes)

some example code to make it clearer:

DB<1> sub tst ($) { print @_} # primitive approach DB<2> @a=qw/a b c/ DB<3> tst @a # NO! I don't want to force scalar c +ontext on other datatypes 3 DB<4> sub tst2 (\$) { print @_} # Restrict to scalars starting with +sigil "$" DB<5> tst2 @a # YES! at least there is no scalar c +ontext Type of arg 1 to main::tst2 must be scalar (not array dereference) at +(eval 11)[/usr/share/perl/5.10/perl5db.pl:638] line 2, at EOF<P><P> DB<6> tst2 "1" # NO! Constants are not allowed ... +sigh Type of arg 1 to main::tst2 must be scalar (not constant item) at (eva +l 9)[/usr/share/perl/5.10/perl5db.pl:638] line 2, at EOF

Any ideas? Am I missing something?

(....I'm not really confident anyhow ...)

Cheers Rolf

UPDATE: yeah I know that I still need to dereference with (\$), but that's not the point here.

UPDATE2: now that's confusing, isn't it?

DB<9> print ref \"1" SCALAR

UPDATE3: (16:25 CEST) Is this a purely academic discussion? Don't think so, let me give you an example of a problem with the command join:

DB<13> @a=qw/a b c/ DB<14> print join ":",@a # RIGHT a:b:c DB<15> print join @a,":" # NONSENSE (at least in 99.9% of th +e cases) but no error message : DB<16> # What if I REALLY want the length as delimiter? DB<17> print join scalar @a,":" # well, in <0.1% of the usecases, yo +u can afford to be explicit :

Many beginners (and even me sometimes) get confused about when to list the delimiter argument. But the compiler doesn't complain about the most obviously wrong syntax and the user hast to debug the problem...

Now the experienced programmers has to explain why an array in scalar context returns a valid argument ... and the newbie gets frustrated, and asks himself why he ever has chosen perl...

IMHO extending the prototype mechanism such that it's possible to catch these cases would really improve perl's acceptance.

Replies are listed 'Best First'.
Re: Prototype for constant items?
by moritz (Cardinal) on Sep 25, 2009 at 12:51 UTC
    I'd like to construct a prototype which checks at compile time for a "real scalar value" instead of forcing scalar context.

    I'd be surprised if that was possible. It also begs the question what to do with test1 other_function

    if not forcing scalar context onto other_function. Since context propagates inwards in Perl 5, other_function has to be called in some context, forcing the the return value of other_function to be in that context.

    What do you need that feature for? It might be an XY Problem

    Perl 6 - links to (nearly) everything that is Perl 6.
      No XY-problem, I want the possibility of a more rigid compile-time checking.

      For instance, I want to get warnings/errors for typos like in  join @a,@a (the length of @a will be used as delimiter here)

      Using \$ excludes functions, if I want functions I can still use \[$&], but there is no way to include constants in this list...

      ... OK I have to admit that functions are a problem, too! (Because of scoping, evaluation a coderef inside the prototyped function will produce other return values than passing the return value....)

      sigh ... seems like I have to rely on something like perl critic to achieve this ..

      hmm writing something like the following is a (very unpractical) workaround :

      DB<18> tst2 ${ \"1" } SCALAR(0x89baac0)

      Cheers Rolf

        I want the possibility of a more rigid compile-time checking

        And therein lies your problem.

        Prototypes are primarily hints for the parser what to expect next, and allow you to write functions that syntactically resemble built-in functions.

        When you abuse them for anything else you're bound to run into limitations rather soon.

        Perl 6 - links to (nearly) everything that is Perl 6.
Re: Prototype for constant items???
by ikegami (Patriarch) on Sep 25, 2009 at 14:35 UTC

    Just like \@ requires that the arg starts with "@", \$ requires that the arg starts with "$".

    foo(${\ "constant" }); foo(${\ bar() }); &foo(\ "constant" ); &foo(\ bar() );

    Perl could be changed to accept constants. (I think it's based on opcodes, not symbols.) However, it defies the expectation of a writable value (\$) implies.

Re: Prototype for constant items???
by Bloodnok (Vicar) on Sep 25, 2009 at 13:22 UTC
    Re: UPDATE2:

    I can't see what's confusing you about \"1" resulting in a SCALAR - I remember reading somewhere, I think it's in one of the O'Reillys, but senility and alcohol together combine to prevent me from remembering exactly where, that a primitive means of constructing a constant relies on this - using idioms of the form:

    *PI = \3.14159; *STR = \'Some string'; # etc.
    Soooo, AFAICT, pre-declaring the literal as a constant solves your problem...
    use warnings; use strict; no strict qw/vars/; *STR = \'Some string'; my ($str1, $str2) = (\'Some string', $STR); sub foo(\$) { return } eval { foo($STR) }; warn "$@"; eval { foo($str1) }; warn "$@"; eval { foo($str2) }; warn "$@";
    Giving
    $ perl tst.pl Warning: something's wrong at tst.pl line 10. Warning: something's wrong at tst.pl line 12. Warning: something's wrong at tst.pl line 14.
    Doesn't it ?

    A user level that continues to overstate my experience :-))
      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

        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.

Re: Prototype for constant items???
by cdarke (Prior) on Sep 25, 2009 at 16:42 UTC
    the newbie gets frustrated, and asks himself why he ever has chosen perl...

    The way I pitch prototypes is that they are for checking references are the correct type, or for setting context - not for what you are trying to do. You are right to say that Perl does not achieve with prototypes what, say, a C programmer would expect (many C programmers don't understand C prototypes either, but that's another discussion). Very few people find a use for Perl 5 prototypes as they stand. Is it worth changing Perl 5 to support them in this way? After all, Rakudo Star is out next spring, and all this will be fixed and sweet (ish).