[go: up one dir, main page]

Skip to content

Utility library dedicated for functional & non-functional codebases to simplify modelling of success and failure responses for Java/Kotlin πŸ”€

License

Notifications You must be signed in to change notification settings

dzikoysk/expressible

Repository files navigation

Expressible CI codecov Maven Central

Dependency free utility library for Java & Kotlin, dedicated for functional codebases that require enhanced response handling. Express yourself with inspired by Rust, Kotlin and Vavr wrappers, to provide better API using this tiny library.


Supported wrappers (in panda.std.* package):

Features Description
Result<Value, Error> solve error handling gracefully, get rid of exception based side-effects
Option<Value> enhanced alternative to standard Optional<Value>
Lazy<Value> lazy values & runners
Completable<Value>
with Publisher & Subscriber
synchronized alternative to CompletableFuture<Value>
Reference<V>,
MutableReference<V>,
Computed
Simple reactive containers
Mono<A>,
Pair<A, B>,
Triple<A, B, C>,
Quad<A, B, C, D>
generic wrappers for set of values
Throwing functions, runnables, suppliers and consumers set of functional interfaces with support for exception signatures
Tri and Quad consumers, functions and predicates additional functional interfaces
PandaStream<Value> Stream<Value> wrapper with support for features provided by expresible library

By default, expressible exposes non-terminating methods, so you can freely divide functions into smaller pieces and move from non-functional codebases without having a heart attack.


dependencies {
    implementation("org.panda-lang:expressible:1.3.6") // Core library
    implementation("org.panda-lang:expressible-kt:1.3.6") // Kotlin extensions
    testImplementation("org.panda-lang:expressible-junit:1.3.6") // JUnit extensions
}

Examples

Suggested snippets show only a small use-cases for the available api. You're not forced to use this library this way, so you may need to find your style in expressing your thoughts. Adopting functional approach requires time and to simplify this process it's easier to slowly introduce new elements based on simple concepts.

Result

Rather than using Exception based error handling, return meaningful errors and interact with api responses gracefully. Following functional programming patterns make sure your methods don't contain side effects and unexpected exit points.

class UserEndpoint {
    // You can use fully functional approach
    fun createUser(request: HttpRequest, response: HttpResponse) =
        userFacade.createUsername(request.param("username"))
            .peek { user -> response.respondWithJsonDto(user) }
            .onError { error -> ErrorReposne(BAD_REQUEST, error) }
}

class UserFacade {
    // You can start adoption in a regular, non-functional codebases
    fun createUser(username: String): Result<User, String> {
        if (userRepository.findUserByName(username).isPresent()) {
            return error("User $username already exists")
        }
        return ok(userRepository.createUser(username))
    }
}

internal class UserFacadeTest : UserSpec {
    // JUnit support
    @Test
    fun `should create user with a valid username` () {
        // given: a valid username
        val username = 'onlypanda'
        // when: user is created with the following name
        val user = userFacade.createUser(username)
        // then: user has been created
        assertOk(username, user.map(User::getUsername))
    }
} 

Option

Similar usage to Optional<Value> type provided by Java:

Option<String> withValue = Option.of("Value");
Option<String> empty = Option.empty();

Lazy

Lazy<String> completed = new Lazy<>("Value");
Lazy<String> lazy = new Lazy<>(() -> "Value");
Lazy<Void> initialize = Lazy.ofRunnable(() -> "Called just once);

String value = completed.get();

Completable

Completable<String> completable = Completable.create();

completable
    .thenApply(value -> parseBoolean(value))
    .then(value -> System.out.println(value));

completable.complete("true");

Reactive

Reference<Integer> a = reference(1);
MutableReference<Integer> b = mutableReference(2);
Computed<Integer> result = computed(dependencies(a, b), () -> a.get() + b.get());

result.subscribe(value -> System.out.println(value));
b.update(3); // prints "4"

Panda Stream

PandaStream<String> empty = PandaStream.empty();
PandaStream<String> standard = PandaStream.of(new ArrayList<>().stream());

Used by