Skip to content

Instantly share code, notes, and snippets.

@Girgias
Last active February 23, 2024 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Girgias/a9d4cd0b595ce1dfa220e103aacdda31 to your computer and use it in GitHub Desktop.
Save Girgias/a9d4cd0b595ce1dfa220e103aacdda31 to your computer and use it in GitHub Desktop.
Moving away from Monads to handle errors
<?php
function generateFunctionsAndClass(int $i)
{
$i1 = $i - 1;
return <<<TEMPLATE
enum E{$i} { case Error; }
class R{$i} {}
function f{$i}(R{$i1} \$input): R{$i}|E{$i} { return new R{$i}; }
function e{$i}(R{$i1} \$input): R{$i}|E{$i} { return E{$i}::Error; }
TEMPLATE;
}
$code = join("\n", [
'class R0 {}',
...array_map(
generateFunctionsAndClass(...), range(1, 5)
)
]);
eval($code);
function isOfSingleType($value, ReflectionNamedType $type): bool
{
$type_name = $type->getName();
if ($type->isBuiltin()) {
return (get_debug_type($value) === $type_name);
} else {
return ($value instanceof $type_name);
}
}
function isOfType($value, ReflectionType $type): bool
{
return match (true) {
$type instanceof ReflectionNamedType => isOfSingleType($value, $type),
$type instanceof ReflectionUnionType => array_reduce(
$type->getTypes(),
fn (bool $carry, ReflectionType $type) => $carry || isOfType($value, $type),
initial: false,
),
$type instanceof ReflectionIntersectionType => array_reduce(
$type->getTypes(),
fn (bool $carry, ReflectionType $type) => $carry && isOfType($value, $type),
initial: true,
),
};
}
/**
* @template T
* @param T $x
* @return T
*/
function identity(mixed $x): mixed {
return $x;
}
/**
* @template T
* @template S
* @template R
* @param Closure(T): S $f
* @param Closure(S): R $g
* @return Closure(T): R
*/
function apply(Closure $f, Closure $g): Closure {
return static fn($v) => $g($f($v));
}
function applyAll(Closure ...$closures): Closure
{
return array_reduce(
$closures,
callback: apply(...),
initial: identity(...)
);
}
function applyIfValid(Closure $f, Closure $g): Closure {
$input_type = (new ReflectionFunction($g))->getParameters()[0]->getType();
return static function($v) use ($f, $g, $input_type) {
$inter = $f($v);
if (isOfType($inter, $input_type)) {
return $g($inter);
} else {
return $inter;
}
};
}
function applyAllIfValid(Closure ...$closures): Closure
{
return array_reduce(
$closures,
callback: applyIfValid(...),
initial: identity(...)
);
}
$g = applyAllIfValid(f1(...), f2(...), e3(...), f4(...), f5(...));
var_dump($g(new R0));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment