return new self($message);
}
+
+ public static function tupleMismatch($patternCount, $valueCount)
+ {
+ $message = 'tupleMismatch(%d != %d)';
+ $message = sprintf($message, $patternCount, $valueCount);
+
+ return new self($message);
+ }
}
namespace Widmogrod\Useful;
+use Widmogrod\Monad\Maybe\Just;
+use Widmogrod\Primitive\Listt;
+use function Widmogrod\Functional\concatM;
use function Widmogrod\Functional\curryN;
+use function Widmogrod\Functional\fromIterable;
+use function Widmogrod\Functional\fromNil;
+use function Widmogrod\Functional\fromValue;
+use function Widmogrod\Functional\zip;
+use function Widmogrod\Monad\Maybe\just;
+use function Widmogrod\Monad\Maybe\nothing;
+
+const any = 'Widmogrod\Useful\PatternAny';
/**
* match :: #{ Pattern -> (a -> b)} -> a -> b
}
foreach ($patterns as $className => $fn) {
- if ($value instanceof $className) {
- return $value instanceof PatternMatcher
- ? $value->patternMatched($fn)
- : $fn($value);
+ $isTuplePattern = is_int($className) && is_array($fn);
+ if ($isTuplePattern) {
+ [$tuple, $fn] = $fn;
+ $result = matchTuple($tuple, $value);
+ if ($result instanceof Just) {
+ return $fn(...$result->extract());
+ }
+ }
+
+ if (isMatch($value, $className)) {
+ return isAny($className)
+ ? $fn($value)
+ : matchApply($value, $fn);
}
}
throw PatternNotMatchedError::cannotMatch($value, array_keys($patterns));
})(...func_get_args());
}
+
+function isMatch($value, $className): bool
+{
+ if (is_array($value)) {
+ return false;
+ }
+
+ if ($value instanceof $className) {
+ return true;
+ }
+
+ return isAny($className);
+}
+
+function isAny($className)
+{
+ return $className === any;
+}
+
+function matchTuple(array $tuplePattern, array $valueTuple)
+{
+ $patternCount = count($tuplePattern);
+ $valueCount = count($valueTuple);
+
+ if ($valueCount !== $patternCount) {
+ return nothing();
+ }
+
+ $collectArgs = function (): Listt {
+ return fromIterable(func_get_args());
+ };
+
+ $args = fromNil();
+ foreach (zip(fromIterable($tuplePattern), fromIterable($valueTuple)) as [$className, $value]) {
+ if (!isMatch($value, $className)) {
+ return nothing();
+ }
+
+ $args = concatM($args, isAny($className) ? fromValue($value) : matchApply($value, $collectArgs));
+ }
+
+ return just(iterator_to_array($args));
+}
+
+function matchApply($value, callable $fn)
+{
+ return $value instanceof PatternMatcher
+ ? $value->patternMatched($fn)
+ : $fn($value);
+}
use Widmogrod\Useful\PatternMatcher;
use Widmogrod\Useful\PatternNotMatchedError;
-use const Widmogrod\Functional\identity;
use function Widmogrod\Useful\match;
+use const Widmogrod\Functional\identity;
+use const Widmogrod\Useful\any;
class MatchTest extends \PHPUnit\Framework\TestCase
{
'$value' => random_int(-1000, 1000),
'$expectedMessage' => 'Cannot match "integer" type. Defined patterns are: "test\Useful\MatchTest", "RandomString"',
],
+ 'Value not in tuple pattern list' => [
+ '$patterns' => [
+ [[self::class, \stdClass::class], identity],
+ [["RandomString"], identity],
+ ],
+ '$value' => [random_int(-1000, 1000)],
+ '$expectedMessage' => 'Cannot match "array" type. Defined patterns are: "0", "1"',
+ ],
];
}
public function providePatterns()
{
$std = new \stdClass();
+ $e = new \Exception();
+ $m = new MyPatternMatcher(100, 123);
return [
'single pattern' => [
'$value' => $std,
'$expected' => $std,
],
+ 'single pattern fallback to any' => [
+ '$patterns' => [
+ \stdClass::class => identity,
+ any => identity,
+ ],
+ '$value' => $e,
+ '$expected' => $e,
+ ],
'many patterns' => [
'$patterns' => [
\Exception::class => identity,
'$value' => $std,
'$expected' => $std,
],
+ 'tuple patterns' => [
+ '$patterns' => [
+ [[\stdClass::class, \stdClass::class], function () {
+ return func_get_args();
+ }],
+ ],
+ '$value' => [$std, $std],
+ '$expected' => [$std, $std],
+ ],
+ 'tuple fallback to any patterns' => [
+ '$patterns' => [
+ [[\stdClass::class, \stdClass::class], function () {
+ return func_get_args();
+ }],
+ [[any, any], function () {
+ return ['any', func_get_args()];
+ }],
+ ],
+ '$value' => [$std, $m],
+ '$expected' => ['any', [$std, $m]],
+ ],
'value as a PatternMatcher patterns' => [
'$patterns' => [
\Exception::class => identity,