Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

Let's pretend: we're writing a markup-translation backend. The front end has taken the input text and transformed it into a bunch of paragraphs, each one a list of (style, content) pairs. For instance, a paragraph might look like

my @test_data = ( ['none', 'The quick brown '], ['bold', 'fox'], ['none', ' jumped '], ['ital', 'over'], ['none', ' the lazy '], ['bold', 'dog'], ['none', '.'] );
Our task is to generate HTML from these paragraphs.

One obvious WTDI is to build a dispatch table of translation functions:

my %handlers = ( 'none' => sub {shift}, 'bold' => \&wrap_with_b_tags, 'ital' => \&wrap_with_i_tags, 'para' => \&wrap_with_p_tags, ); sub build_para { my ($data) = @_; my $text = ''; for (@$data) { $text .= $handlers{$_->[0]}->($_->[1]); } return $handlers{'para'}->($text); }
That's all well and good, but those wrap_with_foo_tags functions are all going to be variations on a theme. That's going to suck if we ever want to extend them (to allow for CSS classes, for instance). What we'd like to be able to do is something like
sub wrap_with_html { my ($tag, $text) = @_; return "<$tag>$text</$tag>"; } my %handlers = ( 'none' => sub {shift}, 'bold' => \&wrap_with_html('b', # partial funcall 'ital' => \&wrap_with_html('i', 'para' => \&wrap_with_html('p', );
but of course that won't work. Will it?

Enter currying. Currying is basically partial application of a function: instead of supplying all of a function's arguments and getting a result back, you supply part of a function's arguments and get a function back. When you apply that curried function to the rest of the arguments, you get the result you expected.

educated_foo has a pretty clever currying implementation here, but for my purposes this should be sufficient:

sub curry { my ($func, @args) = @_; return sub { my (@rest) = @_; &$func(@args, @rest); } } # Currying: sub foo { my ($x, $y) = @_; print "$x + $y = ", $x + $y, "\n"; } my $plus_five = &curry(\&foo, 5); &$plus_five(3);
It's not pretty, nor is it especially safe, but it works.

Now we can have our cake and eat it, too: a pleasantly general wrap_with_html function, a dispatch table full of specialized functions (which, I might add, we can generate from a config file if we so desire), a distinct lack of superfluous wrapper code, and a bunch of coderefs to confuse the newbies. (Okay, that last is a bug, not a feature. That's the major drawback to currying: it's not easy to understand from a procedural point of view.) Our full code looks like this:

sub curry { my ($func, @args) = @_; return sub { my (@rest) = @_; &$func(@args, @rest); } } my %handlers = ( 'none' => sub {shift}, 'bold' => &curry(\&wrap_with_html, 'b'), 'ital' => &curry(\&wrap_with_html, 'i'), 'para' => &curry(\&wrap_with_html, 'p'), ); sub wrap_with_html { my ($tag, $text) = @_; return "<$tag>$text</$tag>"; } sub build_para { my ($data) = @_; my $text = ''; for (@$data) { $text .= $handlers{$_->[0]}->($_->[1]); } return $handlers{'para'}->($text); } my @test_data = ( ['none', 'The quick brown '], ['bold', 'fox'], ['none', ' jumped '], ['ital', 'over'], ['none', ' the lazy '], ['bold', 'dog'], ['none', '.'] ); print &build_para(\@test_data);

I should point out that this is a bit of a toy example: it might be better to use qred regexes in a lookup table instead, for instance. I didn't want to clutter the code with more complexity than I needed; but the nice thing about this approach is that if you ever have to make wrap_with_html more complex, you don't really have to change anything else.

--
F o x t r o t U n i f o r m
Found a typo in this node? /msg me
% man 3 strfry


In reply to Specializing Functions with Currying by FoxtrotUniform

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others browsing the Monastery: (7)
    As of 2015-07-04 22:30 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









      Results (60 votes), past polls