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
Options 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 Results
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.