Wednesday, June 7, 2017

Functional Programming with Phunkie: Building a PHP JSON Parser

Phunkie is a library with functional structures for PHP. In this two-part tutorial, Phunkie’s creator Marcello Duarte, head of training at Inviqa, explains how to create Parser combinators using the functional library. This post first appeared on the Inviqa blog, and was republished here with their permission. For an exploration of PHP's "non-exotic" features, we have great paid course.

In the first part of this series we explored parsers and combinators to help you start getting values from functional programming with PHP. We covered the basics using examples, and now we’ll move onto more sequencing and other strategies.

Let’s pick up where we left off!

Phunkie logo

Sequencing Combinators

Ok, let’s now try a more useful parser. A parser that, given a predicate, keeps the character if it satisfies the predicate and fails otherwise. We will call this parser sat, from satisfies.

        describe("sat", function() {


            it("parses one character when it satisfies the predicate", function(){
                expect(sat("is_numeric")->run("4L"))->toEqual(ImmList(Pair('4', 'L')));
            });


            it("returns Nil when the character does not satisfy the predicate", function(){
                expect(sat("is_numeric")->run("L4"))->toEqual(Nil());
            });

        });

The implementation, using the primitive parsers item, result and zero, looks like this:

function sat(callable $predicate): Parser
{
    return item()->flatMap(function($x) use ($predicate) {
        return $predicate($x) ? result($x) : zero();
    });
}

You can see how the building blocks are now put to work. We call the item parser, flatMap on its result, and apply the predicate. If the predicate succeeds, we return the result parser, which basically lifts the $x into the parser context. Otherwise, zero will just do the same, but with Nil instead of any result.

We can quickly capitalize on sat, by creating a few other sequencing combinators.

    context("Sequencing combinators", function() {
        describe("char", function() {
            it("parses a character", function() {
                expect(char('h')->run("hello"))->toEqual(ImmList(Pair('h', "ello")));
            });
        });

        describe("digit", function() {
            it("parses a digit", function() {
                expect(digit()->run("42"))->toEqual(ImmList(Pair('4', '2')));
            });
        });

        describe("lower", function() {
            it("parses a lowercase character", function() {
                expect(lower()->run("hello"))->toEqual(ImmList(Pair('h', "ello")));
            });
        });

        describe("upper", function() {
            it("parses an upper case character", function() {
                expect(upper()->run("Hello"))->toEqual(ImmList(Pair('H', "ello")));
            });
        });
    });

You will laugh at how simple the implementation is!

function char($c): Parser
{
    return sat(function($input) use ($c) { return $input === $c; });
}

function digit(): Parser
{
    return sat('is_numeric');
}

function lower(): Parser
{
    return sat(function($c) { return ctype_lower($c); });
}

function upper(): Parser
{
    return sat(function($c) { return ctype_upper($c); });
}

Choice Combinators

In the real world of grammars, if we had to return an empty list as soon as the first parser failed, we would not survive. Parsers have to deal with grammatical choice constructs. A very common scenario is either matching one pattern or another pattern. We do that by adding the plus combinator to our arsenal of parsers.

Continue reading %Functional Programming with Phunkie: Building a PHP JSON Parser%


by Marcello Duarte via SitePoint

No comments:

Post a Comment