Pattern matching is the process of checking a series of token against a pattern.
It is different from pattern recognition as the match needs to be exact.
The process does not only match as a switch statement does, it also assigns the value
a bit like the list
construct in PHP, a process called destructuring.
Most functional languages implement it as a core feature. Here is are some small examples in Haskell:
fact :: (Integral a) => a -> a
fact 0 = 1
fact n = n * fact (n-1)
head :: [a] -> a
head xs = case xs of [] -> error "empty list"
(x:_) -> x
firstThree :: [a] -> (a, a, a)
firstThree (x:y:z:_) = (x, y, z)
firstThree _ = error "need at least 3 elements"
If you want to read more about the topic, you can head over to Wikipedia : Pattern matching
composer require functional-php/pattern-matching
As we cannot extend the syntax of PHP, the choice was made to use a syntax based on arrays. The key representes the pattern and the value is the function to call with the value or a constant if you want to do nothing with it.
Let's see how we could implement our 3 Haskell examples:
use FunctionalPHP\PatternMatching as m;
$fact = m\func([
'0' => 1,
'n' => function($n) use(&$fact) {
return $n * $fact($n - 1);
}
]);
$head = m\func([
'(x:_)' => function($x) { return $x; },
'_' => function() { throw new RuntimeException('empty list'); }
]);
$firstThree= m\func([
'(x:y:z:_)' => function($x, $y, $z) { return [$x, $y, $z]; },
'_' => function() { throw new RuntimeException('need at least 3 elements'); }
]);
You can also use the match
function if you want to have a beefed up version of the switch
statement or if you don't like anonymous functions:
use FunctionalPHP\PatternMatching as m;
function factorial($n) {
return m\pmatch([
'0' => 1,
'n' => function($n) use(&$fact) {
return $n * factorial($n - 1);
}
], $n);
}
echo m\pmatch([
'"toto"' => 'first',
'[a, [b, c], d]' => 'second',
'[a, _, (x:xs), c]' => 'third',
'_' => 'default',
], [1, 2, ['a', 'b'], true]);
// third
If you are just interested in destructuring your values, there is also a helper for that:
use FunctionalPHP\PatternMatching as m;
print_r(m\extract([1, 2, ['a', 'b'], true], '[a, _, (x:xs), c]'));
// Array (
// [a] => 1
// [x] => a
// [xs] => Array ( [0] => b )
// [c] => 1
// )
Here is a quick recap of the available patterns:
Name | Format | Example |
---|---|---|
Constant | Any scalar value (int, float, string, boolean) | 1.0 , 42 , "test" |
Variable | identifier |
a , name , anything |
Array | [<pattern>, ..., <pattern>] |
[] , [a] , [a, b, c] |
Cons | (identifier:list-identifier) |
(x:xs) , (x:y:z:xs) |
Wildcard | _ |
_ |
As | identifier@(<pattern>) |
all@(x:xs) |
You can run the test suite for the library using:
composer test
A test report will be available in the reports
directory.
Any contribution welcome :
- Ideas
- Pull requests
- Issues