Skip to content
This repository has been archived by the owner on Apr 11, 2020. It is now read-only.

xp-forge/match

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Matching DSL

Build Status on TravisCI XP Framework Module BSD Licence Required PHP 5.4+ Supports PHP 7.0+ Supports HHVM 3.4+ Latest Stable Version

Fluent API for matching.

Matching

The following outputs two lines, My Team 🔒 and Developers:

Sequence::of([new Group('My Team', Types::$CLOSED), new Group('Developers', Types::$OPEN)])
  ->map((new Match('Group::type'))
    ->when(new IsEqual(Types::$OPEN), function($group) { return $group->name(); })
    ->when(new IsEqual(Types::$CLOSED), function($group) { return $group->name().' 🔒'; })
  )
  ->each('util.cmd.Console::writeLine')
;

If the group's type() method where to return an unhandled group type, e.g. Types::$HIDDEN, an exception would be raised.

Unhandled values

To handle the default case, use the otherwise() method:

$match= (new Match('Group::type'))
  ->when(new IsEqual(Types::$OPEN), function($group) { return $group->name(); })
  ->when(new IsEqual(Types::$CLOSED), function($group) { return $group->name().' 🔒'; })
  ->otherwise(function($group) { return $group->name().' ('.$group->type()->name().')'; })
;

Matching values

For the special case of testing equality, we can use the specialized ValueOf matcher:

$kind= (new ValueOf('Event::weekday'))
  ->when(Day::$SATURDAY, function() { return 'Weekend'; })
  ->when(Day::$SUNDAY, function() { return 'Weekend'; })
  ->otherwise(function() { return 'During the week'; })
;

$display= $kind(new Event('Relax', new Date('2015-01-18')));    // `Weekend`
$display= $kind(new Event('Meeting', new Date('2015-01-19')));  // `During the week`

Matching types

The following is a reimplementation of PHP's serialize function (incomplete, but you get the idea):

$serialize= (new TypeOf())
  ->when(Primitive::$INT, function($value) { return 'i:'.$value.';'; })
  ->when(Primitive::$STRING, function($value) { return 's:'.strlen($value).':"'.$value.'";'; })
  ->when(Type::$ARRAY, function($value, $self) {
    $r= 'a:'.sizeof($value).':{';
    foreach ($value as $key => $val) {
      $r.= $self($key).$self($val);
    }
    return $r.'}';
  })
  ->when(null, function() { return 'N;'; })
;

$serialized= $serialize(1);       // `i:1;`
$serialized= $serialize('Test');  // `s:4:"Test";`
$serialized= $serialize([1, 2]);  // `a:2:{i:0;i:1;i:1;i:2;}`
$serialized= $serialize(null);    // `N;`

The Type::$ARRAY is actually a type union consisting of zero-indexed arrays and maps, both of which are known to PHP as an array (in contrast, the XP Framework only speaks of the first as arrays). We use it here for performance reasons since we don't need to distinguish between the two anyways.

Performance

They KeyOf class is a high-performance alternative to the ValueOf class although it's restricted to integers and strings (it uses them as keys in its backing map).

// Using native if and comparison
$match= function($value) {
  if (0 === $value) {
    return 'No elements';
  } else if (1 === $value) {
    return 'One element';
  } else {
    return $value.' elements';
  }
};

// Using KeyOf class
$match= (new KeyOf())
  ->when(0, function() { return 'No elements'; })
  ->when(1, function() { return 'One element'; })
  ->otherwise(function($value) { return $value.' elements'; })
;

// Using ValueOf class
$match= (new ValueOf())
  ->when(0, function() { return 'No elements'; })
  ->when(1, function() { return 'One element'; })
  ->otherwise(function($value) { return $value.' elements'; })
;

Using 500000 iterations, PHP 5.4 / Windows 8.1:

Invocation Result Native if KeyOf class ValueOf class
$match(0) "No elements" 0.283 secs 0.386 secs (1.36x) 0.566 secs (2.00x)
$match(1) "One element" 0.287 secs 0.385 secs (1.34x) 0.750 secs (2.61x)
$match(2) "2 elements" 0.386 secs 0.500 secs (1.29x) 0.882 secs (2.28x)
$match(100) "100 elements" 0.383 secs 0.524 secs (1.37x) 0.900 secs (2.35x)

Further reading

This library was inspired by Scala's patter matching.