Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

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

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!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • 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?

    What's my password?
    Create A New User
    and all is quiet...

    How do I use this? | Other CB clients
    Other Users?
    Others browsing the Monastery: (3)
    As of 2017-02-19 12:38 GMT
    Find Nodes?
      Voting Booth?
      Before electricity was invented, what was the Electric Eel called?

      Results (292 votes). Check out past polls.