Skip to content

Latest commit

 

History

History
156 lines (107 loc) · 4.92 KB

README.md

File metadata and controls

156 lines (107 loc) · 4.92 KB

Fearless function combination in Java

Maven Central Javadoc License

Lines of code codecov GitHub repo size jar size dependency count

Techniques

Unlock your functional programming potential with these combination techniques:

Fusion

Z.fuse(fn1).fuse(fn2) Combine two functions.

var internedTrim = Z.fuse(String::trim).fuse(String::intern);

assertEquals("hello", internedTrim.apply(" hello "));

// Protip: Interned strings can use == directly.
assertTrue("hello" == internedTrim.apply(" hello "));

Fission

Z.split(fn) Split a multiargs function into a curried form.

var concat = Z.split(String::concat);

assertEquals("hotpot", concat.apply("hot").apply("pot"));

// Protip: "Curried" functions can be partially applied.
var preSomething = concat.apply("pre");

assertEquals("prefix", preSomething.apply("fix"));
assertEquals("presume", preSomething.apply("sume"));

// Protip: Z also has a "flip" function to change order.
var fixedSomething = Z.flip(concat).apply("fix");

assertEquals("prefix", fixedSomething.apply("pre"));
assertEquals("suffix", fixedSomething.apply("suf"));

Assimilation

Z.assimilate[N](curriedFn) Flatten a curried function into a multiargs function.

var checkoutMessage = Z.assimilate2(
    (String item) ->
        (String name) -> String.format("Enjoy your %s, %s!", item, name)
);

assertEquals(
    "Enjoy your bike, Alice!",
    checkoutMessage.apply("bike", "Alice")
);

Absorption

Z.fuse(fn1).absorb(fn2) Unnaturally combine two functions.

This is an evil technique. It's provided as a tool to control evil problems, not to encourage evil code.

var heroes = new ArrayList<>(List.of("joker"));

var emptiedHeroes = Z.fuse(heroes::clear).absorb(() -> heroes);

assertEquals(List.of(), emptiedHeroes.get());

heroes.add("twoface");
emptiedHeroes.get().add("batman");
assertEquals(List.of("batman"), heroes);

Super Fusion

Z.fuse(fn1).fuse(fn2).[...].fuse(fn[N]) Combine N functions. To start with an object or primitive use Z.with(var)...

var isLocalHost = Z.with("https?://localhost(:\\d+)?(/\\S*)?")
    .fuseFunction(Pattern::compile)
    .fuse(Pattern::matcher)
    .fuse(Matcher::matches);

assertTrue(isLocalHost.test("https://localhost:443"));

Note: fuseFunction above is an explicit choice of a single-arg variant of Pattern::compile. Similar fuse[Etc] exist for other functional interfaces.

Z goals

  1. Z only provides function combinators
  2. Z will never need other dependencies
  3. Z will never rely on reflection or code generation
  4. Techniques with the potential to cause problems are annotated as @Evil; choose your path wisely...

Comparisons and advantages

Z fusion

var asciiSum = Z.fuse(String::chars, IntStream::sum);

int sum = asciiSum.applyAsInt("abc");

Equivalent with Function::compose

Function<IntStream, Integer> sumInts = IntStream::sum;
var asciiSum = sumInts.compose(String::chars);

// Captures as a Function<String, Integer> (autoboxing/unboxing occurs)
int sum = asciiSum.apply("abc");

Equivalent with a lambda

// Inference (e.g. with var) is not possible
ToIntFunction<String> asciiSum = s -> s.chars().sum();

int sum = asciiSum.applyAsInt("abc");

Some advantages of Z here:

  1. Tacit yet explicit - Z allows for point-free function combination. This means you state your logic as a fact, and don't worry as much about the exact syntax for instructions. (Of course, Z can accept lambdas!)
  2. Explicit ordering of actions - Z lets you consistently define actions in the order they'll execute.
  3. "Just works" inference - Z techniques are optimized for a wider variety of functional interfaces. It's not necessary to define (or cast) things to a Function<A, B> in order just to expose Function::compose.
  4. Idiomatic functions - Z doesn't reimplement functional interfaces or conventions that already exist. A Predicate will have a test method, a Consumer will have an accept method, etc.

More examples can be found in the Usage Examples in the project's tests.