Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Dynamically Creating a Code reference

by Herkum (Parson)
on Jun 15, 2006 at 21:30 UTC ( #555637=perlquestion: print w/replies, xml ) Need Help??
Herkum has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to dynamically build a Data::FormValidator profile. A short profile would look like this,

my $profile = { required => qw[my_name], constraint_methods => { my_name => sub { my $dfv = shift; my $var = shift; return if ($var !~ /\d+/); return 1; } } }

However, I want take a string and make it into closure in my profile. I am missing something however. If I do this,

$code =<<'END' my $dfv = shift; my $var = shift; return if ($var !~ /\d+/); return 1; END ; my $profile->{constraint_methods}{my_name} = sub { eval $code }; # Doe +s not work

Update: Made the $code not interpolate.

Update No 2: The code above is not working, in that the DFV object is not being returned first to @_.

my $profile->{constraint_methods}{my_name} = sub { use Data::Dump qw(d +ump); warn "PARAMS " . dump(@_) . "\n"; eval $code }; # Does not work

This will dump the DFV object from @_. It seems the eval does not get the whole @_ for some reason. I don't understand why

Update No 3: OK, I found the answer and it had nothing to do with eval, which was working but rather with the validation. There is also a field_filter option used, it does not pass the DFV object, I assumed that it did, I would alter the field value, which screwed up the validation and the constraint code was not run, which is where I thought I was having problems.

Thanks for the help

Replies are listed 'Best First'.
Re: Dynamically Creating a Code reference
by eric256 (Parson) on Jun 15, 2006 at 21:53 UTC

    I don't know how you made that determination but I think something else is happening. The following code shows that the string only gets evaled with the ref itself is called.

    use strict; use warnings; my $code = 'print "Hello\n"'; print "BEFORE\n"; my $test; $test->{a} = sub { eval $code}; print "BETWEEN\n"; $test->{a}->(); print "AFTER\n";


    C:\Perl\test>perl BEFORE BETWEEN Hello AFTER

    You can see the hello is only printed with the ref is executed and not when it is defined.

    Eric Hodges

      Your code works. I am still having a problem however because I am looking for the $dfv object to be passed to the sub {}. I find it passes the variable but it is not passing the DFV object.

      I hate trying to figure out these little quirks!

Re: Dynamically Creating a Code reference
by rblasch (Monk) on Jun 15, 2006 at 22:01 UTC

    First, make sure you do

    use strict; use warnings;

    in your code if you aren't already.

    To be honest, I have not looked into this in detail, but have a look at the $code =<<"END" part. The double quotes will make your string be interpolated, which mean while assigning to $code your variables $dfv etc will be expanded, and may well be undefined. Thus eval might see only my = shift;, which doesn't compile. eval fails and sets $@ aka $EVAL_ERROR, which you should check for.

    Use single quotes around END, or escape everything that might be interpolated.

    Also, you might consider moving the semicolon after the END up, that is:

    $code = <<'END';

    I hope I got it somewhat right, and it helps a bit.

      The actual program is 500 lines long, this is just a sample code to relay the concept, not emulate a complete script.
Re: Dynamically Creating a Code reference
by ioannis (Monsignor) on Jun 16, 2006 at 00:10 UTC
    Here is your solution, with superficial code omitted. There is a closure over the initial value of $var (which can also be set with eval, barring errors). The print statements demonstrate that regardless on how you call the sub, it always acts on its initial closured value.
    my $profile; { my $var = eval '33'; # or just my $var = 33 # or my $var = setme_at_runtime(); $profile = { my_name => sub { ($var =~ /\d+/) ? 1: return } }; } my $fun = $profile->{my_name}; print $profile->{my_name}->( ); print $profile->{my_name}->('a'); print $profile->{my_name}->( 22 );
Re: Dynamically Creating a Code reference
by Tanktalus (Canon) on Jun 15, 2006 at 23:37 UTC

    First thing in my head is that this may be an XY Problem. I think. What is it that you're trying to solve by using a dynamic code reference?

    The reason why I'm not sure is that I do know of uses for dynamic code references - I've done it many times when I'm using input from files and I can conceptualise things better as embedding the data in the code than any other way. But this doesn't appear that way (so far).

    That said, the eval won't happen until it is called by DFV. But I'm not really sure that's what you want.

      Because you don't know how Data::FormValidator works, of course you don't understand. The validation is not done until it is called by DFV, which is what I want.

        Just because I haven't used DFV doesn't mean I don't know how it works. It's still perl, right?

        When you have a code reference, the code being referenced does not get executed until someone dereferences it. It's really that simple. And consistant. Perl always works this way. Always. Well, unless you have a source filter, but I doubt DFV is a source filter (it's too complex).

        This answer just convinces me that it's really an XY Problem - that you don't really want to dynamically create a code reference, instead, you probably want a tutorial on code references and closures instead.

Re: Dynamically Creating a Code reference
by bart (Canon) on Jun 16, 2006 at 07:30 UTC
    What you wanted to do all along is this:
    $coderef = eval "sub { $code }";
    plain and simple. I'm surprised none of the other, many replies suggested as much.

    Check the return value of the eval and/or $@, if the eval fails and returns undef, you must have had a syntax error.

      NOTE, I did not correctly read the parent post! The parent was absolutely correct.

      Sorry, this does not work, you are doing what I did originally, which was

      sub { $code };

      Which treats $code as a string instead of perl code. This of it this way,

      eval sub { 'print "I love perl\n;" }

      this will not return an error and still does nothing except return the the string I just had in there. The eval has to be in the sub for the string.

        I think you did something wrong, perhaps you used single quotes instead of double quotes. As it is in my code, the string is inserted in the sub definition before it is evalled. Try this:
        $code = 'print "I love perl\n";'; $coderef = eval "sub { $code }"; $coderef->(); print "Don't believe me?\n"; $coderef->();
        I love perl Don't believe me? I love perl
Re: Dynamically Creating a Code reference
by badaiaqrandista (Pilgrim) on Jun 16, 2006 at 13:01 UTC

    Why not just create the subroutine reference like this:

    my $code = sub { my $dfv = shift; my $var = shift; return $var !~ /\d+/ ? 1 : 0; }; my $profile = { required => [qw/my_name/], constraint_methods => { my_name => $code, }, };

    No need to create a string first.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://555637]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2017-11-22 22:52 GMT
Find Nodes?
    Voting Booth?
    In order to be able to say "I know Perl", you must have:

    Results (327 votes). Check out past polls.