Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Aren't there code refs as well as function refs?

by dd-b (Monk)
on Mar 03, 2023 at 23:17 UTC ( [id://11150726] : perlquestion . print w/replies, xml ) Need Help??

dd-b has asked for the wisdom of the Perl Monks concerning the following question:

I have clear memories, and I see examples of the idea all over the place. But I can't manage to search up any docs on the concept.

I know how to create a reference to a function, and how to receive it as a parameter and use it (call it), that's all fine.

But isn't there also such a thing as a code ref? What you get if you put { $a = 1 } somewhere, as opposed to the function ref you get from sub { $a = 1 }? So, if a code ref is passed to my function, what does reftype() return for it, and how do I execute it? Pointer to docs rather than writing me something special is fine, I've spent hours looking and not found anything but I'm sure it's out there.

Replies are listed 'Best First'.
Re: Aren't there code refs as well as function refs?
by hippo (Bishop) on Mar 03, 2023 at 23:37 UTC
    What you get if you put { $a = 1 } somewhere, as opposed to the function ref you get from sub { $a = 1 }?

    What you get is an anonymous block - it isn't a reference. It is used primarily (by me, anyway) to limit scope. What you call "function refs" are what I would call code refs.

    Not sure if this is useful to you or not. Perhaps if you could provide a more fully-fledged example it might help?


    🦛

      It may well be useful; if it is useful, it is as evidence towards the hypothesis that things don't work the way I think they do.

      There are a lot of places where anonymous blocks occur in magic being worked by packages. Here's an example from the perldoc for Test::Exception where the throws_ok function uses this:

      throws_ok { read_file( 'unreadable' ) } qr/No file/, 'no file';

      I'm wondering what throws_ok() receives, where, and how. Yes, I notice the missing comma; this is like a print statement where the first optional param (fd in that case) is not separated by a comma from the regular arguments.

        It's obviously using a prototype, the missing comma is typical

        And looking into the sources proves it

        sub throws_ok (&$;$) { my ( $coderef, $expecting, $description ) = @_;

        See my other post for more

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

        The prototype of throw_ok is &$;$, so
        throws_ok { ... } ..., ...;
        is equivalent to
        &throws_ok( sub { ... }, scalar( ... ), scalar( ... ) );
      What you call "function refs" are what I would call code refs.

      I would like to point out a distinction. In a function ref, you cannot just simply modify a variable of the caller except if it is included in the argument list. In a code ref, you could modify the caller's variables but the only way to do this is with the eval() function. Is there any other way to do it? (In the following code, the PrintPattern() function modifies its first argument. But a nicer solution would be to be able to directly modify the caller function's variable.)

      #!/usr/bin/perl use strict; use warnings; my $TRIANGLE = '..Z2.X6.V343.T2132312.RI.P4E4.N8A8.L3636363.J313231323132313.Hc..'; DrawPattern1($TRIANGLE); DrawPattern2($TRIANGLE); DrawPattern3($TRIANGLE); ##################################################################### # This function converts the input pattern from plain text to binary. # Also returns the length of the pattern and a couple of zeros. # Usage: ($PATTERN, $LENGTH, 0, 0) = GetPattern($TEXT) # sub GetPattern { my $PATTERN = defined $_[0] ? $_[0] : ''; # Remove everything except letters and numbers and periods. $PATTERN =~ tr|.0-9A-Za-z||cd; # Replace all numbers and letters with characters 00 - 3E $PATTERN =~ tr|.0-9A-Za-z|\x40\x00-\x3E|; return ($PATTERN, length($PATTERN), 0, 0); } ##################################################################### # This function prints a pattern to stdout. # The pattern should be plain text. Each even numbered letter # specifies the number of spaces to print, and odd numbered # letters specify the number of '#' signs to print. # A period creates a line break. # Usage: DrawPattern1(STRING) # sub DrawPattern1 { my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]); for (my $i = 0; $i < $LENGTH; $i++) { $c = ord(substr($PATTERN, $i, 1)); PrintPattern($MODE, $c); } } ##################################################################### # Prints a number of spaces or pound signs to stdout, or a new line. # Changes the value of the first argument. # Usage: PrintPattern(WHAT_TO_PRINT, COUNT) # sub PrintPattern { @_ == 2 or return; my ($MODE, $COUNT) = @_; $COUNT < 64 or return $_[0] = print "\n"; $_[0] = $MODE = $MODE & 1 ? 32 : 35; print chr($MODE) x $COUNT; } ##################################################################### # This function does exactly the same thing as DrawPattern1() # Usage: DrawPattern2(STRING) # sub DrawPattern2 { my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]); for (my $i = 0; $i < $LENGTH; $i++) { $c = ord(substr($PATTERN, $i, 1)); if ($c == 64) { $MODE = print "\n"; next; } print chr($MODE = $MODE & 1 ? 32 : 35) x $c; } } ##################################################################### # This function does exactly the same thing as DrawPattern1() # Usage: DrawPattern3(STRING) # sub DrawPattern3 { my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]); # This is nice but won't work: my $code_ref = \"if (\$c == 64) { \$MODE = print \"\\n\"; next; }" . " print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; "; # This will work: my $CODE_REF = \"if (\$c == 64) { \$MODE = print \"\\n\"; }" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; } "; for (my $i = 0; $i < $LENGTH; $i++) { $c = ord(substr($PATTERN, $i, 1)); eval($$CODE_REF); # This is, of course, not a very good idea, because # repeatedly using eval() slows down the program. } } #####################################################################

        G'day harangzsolt33,

        "I would like to point out a distinction."

        You have failed to do that. The 108 lines of code that you've posted contain no references to functions, subroutines, or any other code.

        Calling a variable $CODE_REF does not make it a coderef. $CODE_REF is not any kind of reference; it's just a string:

        $ perl -MO=Deparse -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = prin +t \"\\n\"; }" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \ +$c; } ";' my $CODE_REF = 'SCALAR(0xa00038be0) else { print chr($MODE = $MODE & 1 + ? 32 : 35) x $c; } '; -e syntax OK

        You can explicitly check it yourself:

        $ perl -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = print \"\\n\"; } +" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; } "; my +$ref = ref $code_ref; print $ref ? $ref : "not a ref";' not a ref

        Or let Perl do it for you:

        $ perl -Mstrict -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = print \ +"\\n\"; }" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; + } "; $CODE_REF->();' Can't use string ("SCALAR(0xa00038c70) else { print"...) as a subrouti +ne ref while "strict refs" in use at -e line 1.
        "Is there any other way to do it?"

        You can modify a caller's variables by passing references to those variables. Here's a very simple example:

        #!/usr/bin/env perl use strict; use warnings; my ($x, $y) = qw{X Y}; print "BEFORE:\n"; print "\$x[$x] \$y[$y]\n"; mod_callers_vars(\$x, \$y); print "AFTER:\n"; print "\$x[$x] \$y[$y]\n"; sub mod_callers_vars { my ($x_ref, $y_ref) = @_; $$x_ref = 'A'; $$y_ref = 'B'; return; }

        Output:

        BEFORE: $x[X] $y[Y] AFTER: $x[A] $y[B]

        I see LanX has shown you how to use closures (which could be appropriate for more complex scenarios).

        In "Re^4: Aren't there code refs as well as function refs?", you wrote:

        "... you decide to call each function with 30 or so arguments."

        That would be a very poor decision. Instead, use a single hashref argument.

        — Ken

        I read this multiple times and I still have no idea what you are trying to explain with all that code.

        Could you please elaborate?

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

        This will work:
        Can't use string ("SCALAR(0x26afcc8) else { print c"...) as a SCALAR r +ef while "strict refs" in use at garbage.pl line 100.
Re: Aren't there code refs as well as function refs?
by LanX (Saint) on Mar 04, 2023 at 07:04 UTC
    That's a very confusing question: code-ref and function-ref are used synonymously.

    This a ref of a named function

    • \&foo

    This a ref of a anonymous function

    • sub { ... }
    This is a code-block and not (necessarily) in a function, they are important for scooping and as syntactic element like for if, map or sub (sic)
    • { my $x = 1; ... }
    Not to be confused with an anonymous hash ref
    • { $x => 1,... }

    (Code-blocks and anonymous hash references are distinguished by "looks-like guessing")

    Curlies are also used for explicitly marking symbol names after sigils

    • "X${name}X" same as "X" . $name ."X"
    A special syntactic sugar comes with are prototype (&) for arguments. One can skip the sub when passing an anonymous code-ref

    After defining

    • sub bar(&) { ... }
    you can write
    • bar { ... }
    instead of
    • bar sub { ... }
    And calling bar will fail at compile time if anything else than a code ref is passed.

    THAT'S ALL!

    This should be a list of all uses of curly brackets in Perl (omitting sublanguage syntax like regex)

    Please explain what you don't understand.

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

        yeah, but
        • quote-like delimiters are a wildmark which fit everywhere
        • hash-keys - OK you are right - they belong to the already mentioned domain of hashes
        • in-string metas for unicode are a sub-language like the afore-mentioned regex, there might be even more like sprintf or formats
        and I also didn't mention dereferencing @{...} etc., but those belong to the same domain like @{name} and the un-strict symbolic derefs @{"na"."me"}

        in short: outside strings (and offsprings like regex) are curlies meant for the domains of

        • code-blocks
        • hashes
        • grouping of variable-symbols/refs

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

Re: Aren't there code refs as well as function refs?
by kcott (Archbishop) on Mar 04, 2023 at 03:05 UTC

    G'day dd-b,

    I think one of the main issues you may be having here is terminology. At the end of the second paragraph of "perlsub: DESCRIPTION" (my emphasis):

    "Often a function without an explicit return statement is called a subroutine, but there's really no difference from Perl's perspective."

    You're making a distinction between "function refs" and "code refs": they're really just the same thing.

    $ perl -E ' use Scalar::Util "reftype"; sub routine { 1; } my $x = \&routine; say $x; say ref $x; say reftype $x; say sub { 1; }; ' CODE(0xa0004a3c0) CODE CODE CODE(0xa0007e370)

    In your follow-up post, you made reference to this type of syntax (which I've abstracted):

    function { code } args

    Before reading on, you may want to review "perlsub: Prototypes" (noting what the '&' means in a prototype) and prototype(). Also be aware that subroutines/functions do not necessarily have a prototype.

    You'll find that syntax in a number of builtin functions; e.g. grep, map & sort. There's a plethora of examples in the builtin module List::Util. There are also many examples in CPAN modules; such as Test::Exception which you've identified. Here's some example prototypes:

    $ perl -E 'use List::Util; say prototype "List::Util::reduce"' &@ $ perl -E 'use Test::Exception; say prototype "Test::Exception::throws +_ok"' &$;$

    There are two other places where you'll often see '{ ... }': anonymous blocks (see ++hippo's reply) and hashref constructors (with which I'll assume you're familiar).

    — Ken

      Prototypes kept going! I kind of went off them a long time ago, they seemed to not be where things were going. Also--I notice they don't affect indirect and other common kinds of calls; this would look hugely likely to cause immense confusion!

      I can of course dig into the places where I wonder what's going on. That code tends to be over my head, though, so it's a deep dive.

      Perl is always an adventure!

        I avoid prototypes in my code, except for the empty prototype '()' to create "Constant Functions".

        In v5.36.0, the experimental status of subroutine Signatures was removed. You may find this to be a better method to declare parameters. Do note that prototypes and signatures are not different syntaxes for the same thing: follow the link for details. This is something I do like and have been using in all of my personal code since Perl v5.36.0 was released.

        Not that I'm recommending it but, just so you know, you can use both a prototype and a signature with the same subroutine. If you do this, use of the :prototype() attribute is advised to avoid ambiguity.

        — Ken