Pragmatica Lite brings the power of functional programming to Java with zero-dependency monadic types that eliminate null pointer exceptions, unchecked exceptions, and callback hell. Built on Java 25's latest features including sealed interfaces and pattern matching.
Without Pragmatica:
// Traditional Java - prone to NPE and unhandled exceptions
public User getUser(String id) throws UserNotFoundException, DatabaseException {
User user = database.findUser(id); // Can throw exception
if (user == null) { // NPE risk
throw new UserNotFoundException(id);
}
return user;
}With Pragmatica:
// Clean, safe, composable
public Result<User> getUser(String id) {
return database.findUser(id)
.filter(user -> user.isActive())
.map(this::enrichUserData)
.recover(DatabaseError.class, this::handleDatabaseError);
}Option<String> name = Option.of(user.getName())
.map(String::toUpperCase)
.filter(n -> n.length() > 2)
.orElse("Anonymous");Result<Integer> divide(int a, int b) {
return b == 0
? Result.err(new MathError("Division by zero"))
: Result.ok(a / b);
}
// Chain operations safely
Result<String> result = divide(10, 2)
.map(x -> x * 2)
.map(Object::toString)
.recover(MathError.class, err -> "Error: " + err.message());Promise<UserProfile> getUserProfile(String userId) {
return fetchUser(userId)
.flatMap(user -> fetchPreferences(user.id()).map(prefs -> new UserProfile(user, prefs)))
.onSuccess(profile -> cache.store(profile))
.onFailure(error -> logger.error("Failed to load profile", error));
}List<Result<User>> userResults = userIds.stream()
.map(this::fetchUser)
.toList();
// Collect all successes, fail if any operation failed
Result<List<User>> allUsers = Result.allOf(userResults);
// Or collect only successful operations
List<User> validUsers = Result.collectSuccesses(userResults);Pragmatica Lite leverages cutting-edge Java 25 features:
- Sealed Interfaces: Type-safe Result and Option hierarchies
- Pattern Matching: Elegant switch expressions for monadic types
- Records: Immutable data structures throughout
Pragmatica Lite is available on Maven Central. Simply add the dependency to your pom.xml:
<dependency>
<groupId>org.pragmatica-lite</groupId>
<artifactId>core</artifactId>
<version>0.8.3</version>
</dependency>For Gradle users:
implementation 'org.pragmatica-lite:core:0.8.3'import org.pragmatica.lang.*;
public class Example {
// Safe division that never throws
public static Result<Double> safeDivide(double a, double b) {
return b == 0.0
? MathError.divisionByZero().result()
: Result.ok(a / b);
}
// Chaining operations
static void main(String[] args) {
safeDivide(10.0, 2.0)
.map(result -> result * 2) // Transform success value
.fold(
error -> "Error: " + error.message(),
success -> "Result: " + success
);
}
}Handle success and failure without exceptions:
Result<String> result = processData()
.map(String::trim)
.filter(s -> !s.isEmpty())
.recover(ValidationError.class, err -> "default");Eliminate null pointer exceptions:
Option<User> user = findUser(id)
.filter(User::isActive)
.map(User::getProfile);Composable asynchronous programming:
Promise<String> response = httpClient.get(url)
.map(Response::body)
.timeout(Duration.ofSeconds(5))
.onFailure(err -> logger.error("Request failed", err));- Zero Dependencies: No external libraries required
- Type Safety: Leverage Java's type system for correctness
- Performance: Minimal overhead, allocation-free operations
- Composability: Chain operations naturally
- Modern Java: Built for Java 25 and beyond
Explore comprehensive examples in the examples directory:
- Asynchronous data processing with Promise
- Error handling patterns with Result
- Null-safe operations with Option
- Real-world application patterns
| Module | Description |
|---|---|
| core | Core monadic types: Result, Option, Promise |
| integrations/json/jackson | Jackson 3.0 integration for JSON serialization |
| integrations/db/jpa | JPA integration with Promise-based operations |
| integrations/metrics/micrometer | Micrometer metrics for Result/Option/Promise |
| examples | Sample applications and usage patterns |
Serialize and deserialize Result, Option, and Promise types:
<dependency>
<groupId>org.pragmatica-lite</groupId>
<artifactId>jackson</artifactId>
<version>0.8.3</version>
</dependency>var mapper = JsonMapper.jsonMapper()
.withPragmaticaTypes()
.build();
// Result<T> serialization
Result<User> result = fetchUser(id);
mapper.writeAsString(result) // Result<String>
.onSuccess(json -> sendResponse(json));
// Type-safe deserialization
mapper.readString(json, new TypeToken<List<User>>() {})
.onSuccess(users -> processUsers(users));Promise-based JPA operations with typed errors:
<dependency>
<groupId>org.pragmatica-lite</groupId>
<artifactId>jpa</artifactId>
<version>0.8.3</version>
</dependency>var ops = JpaOperations.create(entityManager, JpaError::fromException);
// Transactional operations
Promise<User> result = Transactional.withTransaction(
entityManager,
JpaError::fromException,
tx -> ops.persist(newUser)
.flatMap(saved -> ops.flush().map(_ -> saved))
);
// Typed errors: EntityNotFound, OptimisticLock, DatabaseFailure, etc.
result.onFailure(error -> switch (error) {
case JpaError.EntityNotFound _ -> handleNotFound();
case JpaError.OptimisticLock lock -> handleConflict(lock.entityType());
case JpaError.DatabaseFailure db -> handleDatabaseError(db.cause());
default -> handleUnexpected(error);
});Monitor Result, Option, and Promise operations:
<dependency>
<groupId>org.pragmatica-lite</groupId>
<artifactId>micrometer</artifactId>
<version>0.8.3</version>
</dependency>// Wrap operations with metrics
var wrappedOperation = ResultMetrics.timed(
registry,
"user.fetch",
Tags.of("service", "user-service"),
() -> fetchUser(userId)
);
// Automatic success/failure tracking
Promise<Data> monitored = PromiseMetrics.counted(
registry,
"api.call",
Tags.of("endpoint", "/users"),
apiClient.fetchData()
);We welcome contributions! Please feel free to submit pull requests, report issues, or suggest improvements.
- Java 25 or higher
- Maven 3.9+
Licensed under the Apache License, Version 2.0. See LICENSE for details.