Published February 13, 2017
I'm a big fan of Haskell and one of my favorite feature of this awesome language is pattern matching. If you don't know what's pattern matching, it's this:
data Customer = Student | Individual
10
getPrice Individual = 30
The main advantage is, if I add a possibility in my type, for example data Customer = Student | Individual | Company
, the Haskell compiler will tell me that my function is missing a pattern. I will need to provide a getPrice Company = ?
.
Of course, in PHP we don't have a compiler but we can still have some checks…
The problem
Imagine I have a class for my customer:
By the way, pay attention of how 4 lines of code became 18 lines. But, of course, Haskell is a incomprehensible language ;-)
If I have my unit tests like these:
$customer = new ; // in real life, I would use a name constructor…
$customer->type = Customer::STUDENT;
$this->assertEquals(10, $customer->getPrice());
$customer = new ;
$customer->type = ::INDIVIDUAL;
$this;
If latter, I add a new type const COMPANY = 'company'
in my class, my tests will remain green. If I use this if() {} elseif () {} else {}
in a lot of places, it will be difficult to catch all occurrences.
The solution
Use my small pattern matching library (or build your own, it's only a mater of hours…):
If I add a new type and I change my enumerate definition in one place new Pattern([self::STUDENT, self::INDIVIDUAL, self::COMPANY])
, my previous tests are now failing with a new exception:
MissingPatternsDuringMatch: 'company' was missing during the match.
Expected patterns were 'student', 'individual', 'company' and received patterns were 'student', 'individual'.
Even if, I never wrote a test to check if the price for a company is correct.
Extra features of my library
You can check the test file for all features.
Checks
My match()
function is three simple checks happening every time. It means, it raises an exception even if the pattern could be resolved.
- there is no missing pattern in the array (all cases must have a response)
- there is no extra pattern in the array (if a type is removed, I want to remove all the occurrences of the dead code)
- the value provided is in the pattern list
Callbacks
If your application is doing expensive work for each pattern (more expensive than returning "10" or "30"), you can wrap the result in a callback:
$this;
Callbacks arguments
And you can also give arguments to your callbacks with with
:
Even if, in this case, you could simply use $this->age
inside the callback.