Type Option
represents an optional value: every Option
is either
Option\Some
and contains a value, or Option\None
, and does not. Option
types have a number of uses:
- Initial values
- Return values for functions that are not defined over their entire input range (partial functions)
- Return value for otherwise reporting simple errors, where
None
is returned on error
- Optional class properties
- Swapping things out of difficult situations
Option
s are commonly paired with instanceof Option\{Some|None}
to query the
presence of a value and take action, always accounting for the None
case.
Examples
// @return Option<float>
function divide(float $numerator, float $denominator): Option {
if ($denominator === 0.0) {
return Option\none();
}
return Option\some($numerator / $denominator);
}
// The return value of the function is an option
$result = divide(2.0, 3.0);
// Use instanceof to differentiate between Some & None
if ($result instanceof Option\Some) {
// The division was valid
echo "Result: {$result->unwrap()}";
} else {
echo "Cannot divide by 0";
}
// @prints Result: 0.66666666666667
Result<T, E>
is the type used for returning and propagating errors. It two
variants, Ok(T)
, representing success and containing a value, and Err(E)
,
representing error and containing an error value.
Functions return Result
whenever errors are expected and recoverable.
A simple function returning Result might be defined and used like so:
// @param Result<int,string>
function parse_version(string $header): Result {
return match ($header[0] ?? null) {
null => Result\err("invalid header length"),
"1" => Result\ok(1),
"2" => Result\ok(2),
default => Result\err("invalid version"),
};
}
$version = parse_version("1.x");
if ($version->isOk()) {
echo "working with version: {$version->unwrap()}";
} else {
echo "error parsing header: {$version->unwrapErr()}";
}
// @prints working with version: 1
Results must be used
A common problem with using return values to indicate errors is that it is easy
to ignore the return value, thus failing to handle the error. Unused Result
s
are tracked and will trigger an exception when a Result
value is ignored and
goes out of scope. This makes Result
especially useful with functions that may
encounter errors but don’t otherwise return a useful value.
// Write $data in $filepath
// @return Result<int,string> The number of bytes that were written to the file if Ok, an error message otherwise
function writeInFile(string $filepath, string $data): Result {
$res = @file_put_contents($filepath, $data);
if ($res === false) {
return Result\err("failed to write in $filepath");
}
return Result\ok($res);
}
writeInFile("/path/to/file", "Hi!");
// @throws TH\Maybe\Result\UnusedResultException Unused Result dropped
Using a Result
can be done by calling any method on it, except inspect()
& inspectErr()
.
Note: some methods return another Result
that must also be used.