Reply to comment

Shorter code with functional-style programming in PHP.

So, I'm doing wordpress programming, and one of the headaches is that they store serialized objects in the database with the update_option() and related functions.

This makes querying some data difficult. Here's an example of the data structure I was dealing with (it stores sidebar settings).

Array
(
    [wp_inactive_widgets] => Array
        (
        )

    [sidebar-1] => Array
        (
            [0] => search-2
            [1] => recent-posts-2
            [2] => recent-comments-2
            [3] => archives-2
            [4] => categories-2
            [5] => meta-2
        )

)

I'm interested in sidebar-1 because it has an element with the value meta-*.

Here's the code that returns the name of the sidebar in $the_sidebar:

foreach($sidebars as $key=>$sb) {
        foreach($sb as $widgets) {
            if (0===strpos($widgets, 'meta-')) {
                $the_sidebar = $key;
                break;
            }
        }
    }
}

What the heck does it do? I know it now, but won't know it in a week. I'm putting this subproject aside to focus on another subproject, so I want the code to be at least a little comprehensible. I used comments, but this seemed like a good opportunity to use some functional programming.

Here's the functional version.

    $function = Fgrepassoc(Fmatchinarray('/meta-\d+/'));
    $the_sidebar = array_keys($function($sidebars))[0];

And here's the code for the two functions that start with "F":

/**
 * Returns the array if the array contains the pattern in the values.
 */
function Fmatchinarray($pattern) {
    return function ($a) use ($pattern) {
        foreach(array_values($a) as $v) {
            if (preg_match($pattern, $v)) return $a;
        }
    };
}
/**
 * Returns an associative array with pairs where the key satisfies the function.
 */
function Fgrepkeyassoc($func) {
    return function ($a) use ($func) {
        $output = array();
        foreach($a as $k=>$v) {
            $r = $func($k);
            if ($r) {
                $output[$k] = $v;
            }
        }
        return $output;
    };
}

Warning - there are some bugs related to truthyness in there. I haven't figured them out yet, but they're related to 0 and '' being considered false.

The code for the "F" functions is longer, but the application code is now a two-line program. We've gone from 5 lines of code to 2 lines. That's if we ignore closing brackets.

Not only that, but the assignment to $the_sidebar is now outside the loops, rather than buried in the center of two loops.

There aren't any loops, either!

What's Happening

The first line is unusual. What it does is create a function.

    $function = Fgrepassoc(Fmatchinarray('/meta-\d+/'));

Both functions that start with "F" return a function (rather than a value).

Fmatchinarray returns a function that searches for '/meta-\d+/' in an array, and returns the entire array if it matches.

So if you did $f = Fmatchinarray('hi'); you would get a function $f, that can run like this: $f(array('hi')); It would return the array('hi').

Fgrepassoc is similar, and returns a function that loops over an array and produces another array. Instead of matching a pattern, it executes a function on the value of the element (in an associative array, aka a hash or dict, there's a key and a value -- this would test the value). It does this to the entire array, and returns an array comprising elements for which the function was truthy.

(In Unix, "grep" is a tool that matches strings within a file. "grep magic" would return all the lines in the file that contain "magic".)

Fmatchinarray() matches one. Fgrepassoc() matches many, by running a function that matches one.

So, again, this line basically says "a function that returns an array that contains elements that have an array that matches 'meta-\d+'."

    $function = Fgrepassoc(Fmatchinarray('/meta-\d+/'));

The next line executes the function, pulls out the keys, and then returns the first one.

    $the_sidebar = array_keys($function($sidebars))[0];

Excuses

My naming convention needs some work, and the semantics are a little odd WRT truthy values, and maybe match should return True or False, but my point is simple: functional programming helps you reduce code size, avoid errors, and once you get the hang of it, write programs faster.

There's a functional library for PHP that's similar to Underscore. Unlike what I did, they treat functions that return functions as a special feature called partial application.

Reply

The content of this field is kept private and will not be shown publicly.
  • Lines and paragraphs break automatically.

More information about formatting options

6 + 1 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.