Maven:
<dependency> <groupId>org.pragmatica-lite</groupId> <artifactId>core</artifactId> <version>1.0.0-rc1</version> </dependency>
Gradle:
implementation 'org.pragmatica-lite:core:1.0.0-rc1'
Documentation: https://central.sonatype.com/artifact/org.pragmatica-lite/core
| From | To | Method |
|---|---|---|
Option<T> |
Result<T> |
.toResult(Cause cause) or .await(Cause cause) |
Option<T> |
Result<T> |
.toResult() (uses CoreError.emptyOption) |
Option<T> |
Promise<T> |
.async(Cause cause) |
Option<T> |
Promise<T> |
.async() (uses CoreError.emptyOption) |
Option<T> |
Optional<T> |
.toOptional() |
| From | To | Method |
|---|---|---|
Result<T> |
Option<T> |
.option() (loses error) |
Result<T> |
Promise<T> |
.async() |
| From | To | Method |
|---|---|---|
Promise<T> |
Promise<T> |
.async() (identity) |
Promise<T> |
Result<T> |
.await() (blocks) |
Promise<T> |
Result<T> |
.await(TimeSpan timeout) |
| From | To | Method |
|---|---|---|
Cause |
Result<T> |
.result() |
Cause |
Promise<T> |
.promise() |
Option.some(value) // Create present option Option.present(value) // Alias for some() Option.none() // Create empty option Option.empty() // Alias for none() Option.option(nullable) // null -> none, value -> some Option.from(Optional<T>) // Convert Java Optional
Result.success(value) // Create success Result.ok(value) // Alias for success() Result.unitResult() // Success with Unit value Result.failure(cause) // Create failure (prefer cause.result()) Result.err(cause) // Alias for failure()
Promise.success(value) // Resolved success Promise.ok(value) // Alias for success() Promise.unitPromise() // Resolved with Unit Promise.failure(cause) // Resolved failure (prefer cause.promise()) Promise.resolved(Result<T> result) // Resolved from result Promise.promise() // Unresolved promise Promise.promise(Consumer<Promise<T>>) // Unresolved, runs consumer async Promise.promise(Supplier<Result<T>>) // Async execution of supplier
// Basic lift Result.lift(ThrowingFn0<U> supplier) Result.lift(Fn1<Cause, Throwable> mapper, ThrowingFn0<U> supplier) Result.lift(ThrowingRunnable runnable) // Returns Result<Unit> Result.lift(Cause cause, ThrowingFn0<U> supplier) // Fixed cause // Direct invocation (lift + apply) Result.lift1(ThrowingFn1<R, T1> fn, T1 value) Result.lift2(ThrowingFn2<R, T1, T2> fn, T1 v1, T2 v2) Result.lift3(ThrowingFn3<R, T1, T2, T3> fn, T1 v1, T2 v2, T3 v3) // Function factories (returns wrapped function) Result.liftFn1(ThrowingFn1<R, T1> fn) // Returns Fn1<Result<R>, T1> Result.liftFn2(ThrowingFn2<R, T1, T2> fn) Result.liftFn3(ThrowingFn3<R, T1, T2, T3> fn)
// Basic lift (async execution) Promise.lift(ThrowingFn0<U> supplier) Promise.lift(Fn1<Cause, Throwable> mapper, ThrowingFn0<U> supplier) Promise.lift(ThrowingRunnable runnable) // Returns Promise<Unit> // Direct invocation (async) Promise.lift1(ThrowingFn1<R, T1> fn, T1 value) Promise.lift2(ThrowingFn2<R, T1, T2> fn, T1 v1, T2 v2) Promise.lift3(ThrowingFn3<R, T1, T2, T3> fn, T1 v1, T2 v2, T3 v3) // Function factories Promise.liftFn1(ThrowingFn1<R, T1> fn) // Returns Fn1<Promise<R>, T1>
Result.all(Result<T1>) // -> Mapper1<T1> Result.all(Result<T1>, Result<T2>) // -> Mapper2<T1, T2> // ... up to Mapper9 Result.allOf(Result<T>...) // -> Result<List<T>> Result.allOf(List<Result<T>>) // -> Result<List<T>>
Promise.all(Promise<T1>) // -> Mapper1<T1> Promise.all(Promise<T1>, Promise<T2>) // -> Mapper2<T1, T2> // ... up to Mapper9 Promise.allOf(Collection<Promise<T>>) // -> Promise<List<Result<T>>>
Option.any(Option<T>...) // First present option Result.any(Result<T>...) // First success result Promise.any(Promise<T>...) // First success, cancels others
// All types .map(Fn1<U, T> mapper) // Transform value .map(Supplier<U> supplier) // Replace value .flatMap(Fn1<M<U>, T> mapper) // Chain monadic operations .flatMap(Supplier<M<U>> supplier) // Replace with monadic value // Result/Promise only .flatMap2(Fn2<M<U>, T, I> mapper, I param) // flatMap with extra param .mapToUnit() // Transform to Unit
.filter(Cause cause, Predicate<T> predicate) .filter(Fn1<Cause, T> causeMapper, Predicate<T> predicate)
// Option .onPresent(Consumer<T>) .onEmpty(Runnable) .apply(Consumer<T>, Runnable) // Bifurcation // Result .onSuccess(Consumer<T>) .onFailure(Consumer<Cause>) .onResult(Consumer<Result<T>>) .apply(Consumer<T>, Consumer<Cause>) // Bifurcation // Promise (same as Result, plus async variants) .onSuccessAsync(Consumer<T>) .onFailureAsync(Consumer<Cause>) .withSuccess(Consumer<T>) // Returns self for chaining
// Option .fold(Supplier<R> empty, Fn1<R, T> present) // Result/Promise .fold(Fn1<R, Cause> failure, Fn1<R, T> success)
.or(T replacement) // Option/Result: fallback value .or(Supplier<T> supplier) // Option/Result: lazy fallback .orElse(M<T> replacement) // Fallback monadic value .recover(Fn1<T, Cause> mapper) // Result/Promise: recover from failure
// Null check Verify.Is::notNull // Presence check (CharSequence) Verify.Is::present // Not null and not blank // String checks Verify.Is::empty Verify.Is::notEmpty Verify.Is::blank Verify.Is::notBlank Verify.Is::lenBetween // lenBetween(8, 128) Verify.Is::contains Verify.Is::notContains Verify.Is::matches // matches(Pattern) // Numeric checks Verify.Is::positive Verify.Is::negative Verify.Is::nonNegative Verify.Is::nonPositive Verify.Is::greaterThan Verify.Is::lessThan Verify.Is::between // between(0, 150) Verify.Is::equalTo Verify.Is::notEqualTo
Usage:
Verify.ensure(password, Verify.Is::lenBetween, 8, 128) Verify.ensure(age, Verify.Is::between, 0, 150)
Number.parseInt(String) // -> Result<Integer> Number.parseLong(String) // -> Result<Long> Number.parseDouble(String) // -> Result<Double> Number.parseBigDecimal(String) // -> Result<BigDecimal>
DateTime.parseLocalDate(String) // -> Result<LocalDate> DateTime.parseLocalTime(String) // -> Result<LocalTime> DateTime.parseLocalDateTime(String) // -> Result<LocalDateTime> DateTime.parseInstant(String) // -> Result<Instant>
Network.parseUUID(String) // -> Result<UUID> Network.parseURL(String) // -> Result<URL> Network.parseURI(String) // -> Result<URI>
Causes.cause(String message) // Simple cause Causes.cause(String message, Option<Cause> source) // Cause with source Causes.fromThrowable(Throwable) // Convert exception Causes.forOneValue(String template) // Fn1<Cause, T> factory Causes.forTwoValues(String template) // Fn2<Cause, T1, T2> Causes.forThreeValues(String template) // Fn3<Cause, T1, T2, T3> Causes.composite(Result<?>...) // Composite from results
Template syntax (uses String.format):
Causes.forOneValue("Invalid email: %s") // CORRECT Causes.forTwoValues("Range error: %s to %s") // CORRECT
// Resolution .resolve(Result<T>) // Resolve unresolved promise .succeed(T value) // Resolve with success .fail(Cause cause) // Resolve with failure .cancel() // Cancel execution // Query .isResolved() // Check if resolved // Timeout .timeout(TimeSpan duration) // Add timeout // Result manipulation .mapResult(Fn1<Result<U>, Result<T>>) // Transform result .trace(Fn1<Cause, Cause>) // Transform error (alias: mapError)
public Promise<User> findUser(UserId id) { return Promise.lift( CoreError::database, () -> jdbcTemplate.queryForObject( "SELECT * FROM users WHERE id = ?", new Object[]{id.value()}, this::mapUser) ); }
public Promise<Response> execute(Request request) { return ValidRequest.validRequest(request) // Result<ValidRequest> .async() // Promise<ValidRequest> .flatMap(step1::apply) .flatMap(step2::apply); }
@Test void validation_fails_forInvalidInput() { ValidRequest.validRequest(invalidRequest) .onSuccess(Assertions::fail); } @Test void validation_succeeds_forValidInput() { ValidRequest.validRequest(validRequest) .onFailure(Assertions::fail) .onSuccess(valid -> { assertEquals("expected", valid.field()); }); }