Validation is a data type that represents computations that might fail. Unlike Either, Validation can accumulate errors using a Semigroup instance.
Validation has two variants:
Success(a): Represents a successful computation with value aFailure(e): Represents a failed computation with error eabstract class Validation implements Applicative, Monad, Kind, Foldable {
abstract public function getOrElse($default);
abstract public function map(callable $f): Kind;
}
use Phunkie\Validation\Success;
use Phunkie\Validation\Failure;
$success = Success(42);
$failure = Failure("Invalid input");
// Using Nel constructors
$successNel = SuccessNel(1, 2, 3); // Success(Nel(1, 2, 3))
$failureNel = FailureNel("e1", "e2"); // Failure(Nel("e1", "e2"))
$validation = Either("Invalid email")(
fn() => filter_var($email, FILTER_VALIDATE_EMAIL)
);
$validation = Attempt(function() {
if (!file_exists("config.php")) {
throw new \Exception("Config file not found");
}
return require "config.php";
});
// Single error
$failure = Failure("Invalid input");
// Multiple errors using Nel constructor
$failure = FailureNel("Error 1", "Error 2");
// Combining failures automatically creates Nel
$f1 = Failure("Error 1");
$f2 = Failure("Error 2");
$result = $f1->combine($f2);
// Failure(Nel("Error 1", "Error 2"))
// Combining with existing Nel
$f1 = FailureNel("Error 1", "Error 2");
$f2 = Failure("Error 3");
$result = $f1->combine($f2);
// Failure(Nel("Error 1", "Error 2", "Error 3"))
Transform successful values:
$success = Success(42)->map(fn($x) => $x * 2); // Success(84)
$failure = Failure("error")->map(fn($x) => $x * 2); // Failure("error")
Chain validations:
$validateAge = fn($age) =>
$age >= 18 ? Success($age) : Failure("Must be 18 or older");
$success = Success(20)->flatMap($validateAge); // Success(20)
$failure = Success(16)->flatMap($validateAge); // Failure("Must be 18 or older")
Provide default values:
$success = Success(42)->getOrElse(0); // 42
$failure = Failure("error")->getOrElse(0); // 0
Accumulate errors or combine successes:
$v1 = Success("Hello");
$v2 = Success("World");
// Combine successes
$result = $v1->combine($v2);
// Success(Nel("Hello", "World"))
// Combine failures
$f1 = Failure("Error 1");
$f2 = Failure("Error 2");
$result = $f1->combine($f2);
// Failure(Nel("Error 1", "Error 2"))
$validateName = fn($name) =>
strlen($name) >= 2
? Success($name)
: Failure("Name too short");
$validateEmail = fn($email) =>
filter_var($email, FILTER_VALIDATE_EMAIL)
? Success($email)
: Failure("Invalid email");
$validateAge = fn($age) =>
$age >= 18
? Success($age)
: Failure("Must be 18 or older");
// Combine validations
$form = Success(['name' => 'John', 'email' => 'john@example.com', 'age' => 25]);
$result = $form
->map(fn($data) => $validateName($data['name']))
->flatMap(fn($_) => $validateEmail($data['email']))
->flatMap(fn($_) => $validateAge($data['age']));
$validations = ImmList(
Success(1),
Failure("Error 1"),
Failure("Error 2")
);
apply(...$validations); // Failure("Error 1Error 2")
$result = Attempt(function() {
$config = require "config.php";
$db = new PDO($config['dsn']);
return $db->query("SELECT * FROM users");
});
combine or applyAttempt for exception handlingNel for non-empty error lists