λ
======
[](https://travis-ci.org/palatable/lambda)
[](http://search.maven.org/#search%7Cga%7C1%7Ccom.jnape.palatable.lambda)
Functional patterns for Java 8
#### Table of Contents
- [Background](#background)
- [Installation](#installation)
- [Examples](#examples)
- [Semigroups](#semigroups)
- [Monoids](#monoids)
- [Functors](#functors)
- [Bifunctors](#bifunctors)
- [Profunctors](#profunctors)
- [Applicatives](#applicatives)
- [Traversables](#traversables)
- [ADTs](#adts)
- [HLists](#hlists)
- [Tuples](#tuples)
- [HMaps](#hmaps)
- [CoProducts](#coproducts)
- [Either](#either)
- [Lenses](#lenses)
- [Notes](#notes)
- [License](#license)
Background
----------
Lambda was born out of a desire to use some of the same canonical functions (e.g. `unfoldr`, `takeWhile`, `zipWith`) and functional patterns (e.g. `Functor` and friends) that are idiomatic in other languages and make them available for Java.
Some things a user of lambda most likely values:
- Lazy evaluation
- Immutability by design
- Composition
- Higher-level abstractions
- Parametric polymorphism
Generally, everything that lambda produces is lazily-evaluated (except for terminal operations like `reduce`), immutable (except for `Iterator`s, since it's effectively impossible), composable (even between different arities, where possible), foundational (maximally contravariant), and parametrically type-checked (even where this adds unnecessary constraints due to a lack of higher-kinded types).
Although the library is currently (very) small, these values should always be the driving forces behind future growth.
Installation
------------
Add the following dependency to your:
`pom.xml` ([Maven](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)):
```xml
com.jnape.palatable
lambda
1.6.0
```
`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)):
```gradle
compile group: 'com.jnape.palatable', name: 'lambda', version: '1.6.0'
```
Examples
--------
First, the obligatory `map`/`filter`/`reduce` example:
```Java
Integer sumOfEvenIncrements =
reduceLeft((x, y) -> x + y,
filter(x -> x % 2 == 0,
map(x -> x + 1, asList(1, 2, 3, 4, 5))));
//-> 12
```
Every function in lambda is [curried](https://www.wikiwand.com/en/Currying), so we could have also done this:
```Java
Fn1, Integer> sumOfEvenIncrementsFn =
map((Integer x) -> x + 1)
.then(filter(x -> x % 2 == 0))
.then(reduceLeft((x, y) -> x + y));
Integer sumOfEvenIncrements = sumOfEvenIncrementsFn.apply(asList(1, 2, 3, 4, 5));
//-> 12
```
How about the positive squares below 100:
```Java
Iterable positiveSquaresBelow100 =
takeWhile(x -> x < 100, map(x -> x * x, iterate(x -> x + 1, 1)));
//-> [1, 4, 9, 16, 25, 36, 49, 64, 81]
```
We could have also used `unfoldr`:
```Java
Iterable positiveSquaresBelow100 = unfoldr(x -> {
int square = x * x;
return square < 100 ? Optional.of(tuple(square, x + 1)) : Optional.empty();
}, 1);
//-> [1, 4, 9, 16, 25, 36, 49, 64, 81]
```
What if we want the cross product of a domain and codomain:
```Java
Iterable> crossProduct =
take(10, cartesianProduct(asList(1, 2, 3), asList("a", "b", "c")));
//-> (1,"a"), (1,"b"), (1,"c"), (2,"a"), (2,"b"), (2,"c"), (3,"a"), (3,"b"), (3,"c")
```
Let's compose two functions:
```Java
Fn1 add = x -> x + 1;
Fn1 subtract = x -> x -1;
Fn1 noOp = add.then(subtract);
// same as
Fn1 alsoNoOp = subtract.compose(add);
```
And partially apply some:
```Java
Fn2 add = (x, y) -> x + y;
Fn1 add1 = add.apply(1);
add1.apply(2);
//-> 3
```
And have fun with 3s:
```Java
Iterable> multiplesOf3InGroupsOf3 =
take(3, inGroupsOf(3, unfoldr(x -> Optional.of(tuple(x * 3, x + 1)), 1)));
//-> [[3, 6, 9], [12, 15, 18], [21, 24, 27]]
```
Check out the [tests](https://github.com/palatable/lambda/tree/master/src/test/java/com/jnape/palatable/lambda/functions/builtin) or [javadoc](http://palatable.github.io/lambda/javadoc/) for more examples.
Semigroups
----
[Semigroups](https://en.wikipedia.org/wiki/Semigroup) are supported via `Semigroup`, a subtype of `Fn2`, and add left and right folds over an `Iterable`.
Lambda ships some default logical semigroups for lambda types and core JDK types. Common examples are:
- `AddAll` for concatenating two `Collection`s
- `Collapse` for collapsing two `Tuple2`s together
- `Merge` for merging two `Either`s using left-biasing semantics
Check out the [semigroup](https://palatable.github.io/lambda/javadoc/com/jnape/palatable/lambda/semigroup/builtin/package-summary.html) package for more examples.
Monoids
----
[Monoids](https://en.wikipedia.org/wiki/Monoid) are supported via `Monoid`, a subtype of `Semigroup` with an `A #identity()` method, and add left and right reduces over an `Iterable`.
Some commonly used lambda monoid implementations include:
- `Present` for merging together two `Optional`s
- `Join` for joining two `String`s
- `And` for logical conjunction of two `Boolean`s
- `Or` for logical disjunction of two `Boolean`s
Additionally, instances of `Monoid` can be trivially synthesized from instances of `Semigroup` via the `Monoid#monoid` static factory method, taking the `Semigroup` and the identity element `A` or a supplier of the identity element `Supplier`.
Check out the [monoid](https://palatable.github.io/lambda/javadoc/com/jnape/palatable/lambda/monoid/builtin/package-summary.html) package for more examples.
Functors
----
Functors are implemented via the `Functor` interface, and are sub-typed by every function type that lambda exports, as well as many of the [ADTs](#adts).
Examples of functors include:
- `Fn*`, `Semigroup`, and `Monoid`
- `SingletonHList` and `Tuple*`
- `Choice*`
- `Either`
- `Const`, `Identity`, and `Compose`
- `Lens`
Implementing `Functor` is as simple as providing a definition for the covariant mapping function `#fmap` (ideally satisfying the [two laws](https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Functor.html)).
### Bifunctors
Bifunctors -- functors that support two parameters that can be covariantly mapped over -- are implemented via the `Bifunctor` interface.
Examples of bifunctors include:
- `Tuple*`
- `Choice*`
- `Either`
- `Const`
Implementing `Bifunctor` requires implementing *either* `biMapL` and `biMapR` *or* `biMap`. As with `Functor`, there are a [few laws](https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Bifunctor.html) that well-behaved instances of `Bifunctor` should adhere to.
### Profunctors
Profunctors -- functors that support one parameter that can be mapped over contravariantly, and a second parameter that can be mapped over covariantly -- are implemented via the `Profunctor` interface.
Examples of profunctors include:
- `Fn*`
- `Lens`
Implementing `Profunctor` requires implementing *either* `diMapL` and `diMapR` *or* `diMap`. As with `Functor` and `Bifunctor`, there are [some laws](https://hackage.haskell.org/package/profunctors-5.2/docs/Data-Profunctor.html) that well behaved instances of `Profunctor` should adhere to.
### Applicatives
Applicative functors -- functors that can be applied together with a 2-arity or higher function -- are implemented via the `Applicative` interface.
Examples of applicative functors include:
- `Fn*`, `Semigroup`, and `Monoid`
- `SingletonHList` and `Tuple*`
- `Choice*`
- `Either`
- `Const`, `Identity`, and `Compose`
- `Lens`
In addition to implementing `fmap` from `Functor`, implementing an applicative functor involves providing two methods: `pure`, a method that lifts a value into the functor; and `zip`, a method that applies a lifted function to a lifted value, returning a new lifted value. As usual, there are [some laws](https://hackage.haskell.org/package/base-4.9.1.0/docs/Control-Applicative.html) that should be adhered to.
For example, imagine we have an `Fn2` we'll call `join` that simply joins two strings. If we wanted to join the `String`s wrapped inside two `Either` instances, without `Applicative`, we would at best only be able to `fmap` one while `fmap`ping the other, resulting in an `Either>`.
With `Applicative`, however, we can `fmap(join)` the first `Either`, resulting in an `Either>`, and then `zip` the second `Either`, producing another `Either`. In this way, `Applicative` can be thought of as facilitating `fmap`ping a functor with a multi-arity function.
### Traversables
Traversable functors -- functors that can be "traversed from left to right" -- are implemented via the `Traversable` interface.
Examples of traversable functors include:
- `SingletonHList` and `Tuple*`
- `Choice*`
- `Either`
- `Const` and `Identity`
- `TraversableIterable` for wrapping `Iterable` in an instance of `Traversable`
- `TraversableOptional` for wrapping `Optional` in an instance of `Traversable`
In addition to implementing `fmap` from `Functor`, implementing a traversable functor involves providing an implementation of `traverse`.
As always, there are [some laws](https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Traversable.html) that should be observed.
ADTs
----
Lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type).
### Heterogeneous Lists (HLists)
HLists are type-safe heterogeneous lists, meaning they can store elements of different types in the same list while facilitating certain type-safe interactions.
The following illustrates how the linear expansion of the recursive type signature for `HList` prevents ill-typed expressions:
```Java
HCons> hList = HList.cons(1, HList.cons("foo", HList.nil()));
System.out.println(hList.head()); // prints 1
System.out.println(hList.tail().head()); // prints "foo"
HNil nil = hList.tail().tail();
//nil.head() won't type-check
```
#### Tuples
One of the primary downsides to using `HList`s in Java is how quickly the type signature grows.
To address this, tuples in lambda are specializations of `HList`s up to 5 elements deep, with added support for index-based accessor methods.
```Java
HNil nil = HList.nil();
SingletonHList singleton = nil.cons(5);
Tuple2 tuple2 = singleton.cons(4);
Tuple3 tuple3 = tuple2.cons(3);
Tuple4 tuple4 = tuple3.cons(2);
Tuple5 tuple5 = tuple4.cons(1);
System.out.println(tuple2._1()); // prints 4
System.out.println(tuple5._5()); // prints 5
```
Additionally, `HList` provides convenience static factory methods for directly constructing lists of up to 5 elements:
```Java
SingletonHList singleton = HList.singletonHList(1);
Tuple2 tuple2 = HList.tuple(1, 2);
Tuple3 tuple3 = HList.tuple(1, 2, 3);
Tuple4 tuple4 = HList.tuple(1, 2, 3, 4);
Tuple5 tuple5 = HList.tuple(1, 2, 3, 4, 5);
```
`Index` can be used for type-safe retrieval and updating of elements at specific indexes:
```Java
HCons>> hList = cons(1, cons("2", cons('3', nil())));
HCons> tuple = tuple(1, "2", '3');
Tuple5 longerHList = tuple(1, "2", '3', 4.0d, false);
Index>>> characterIndex =
Index.index().after().after();
characterIndex.get(hList); // '3'
characterIndex.get(tuple); // '3'
characterIndex.get(longerHList); // '3'
characterIndex.set('4', hList); // HList{ 1 :: "2" :: '4' }
```
Finally, all `Tuple*` classes are instances of both `Functor` and `Bifunctor`:
```Java
Tuple2 mappedTuple2 = tuple(1, 2).biMap(x -> x + 1, Object::toString);
System.out.println(mappedTuple2._1()); // prints 2
System.out.println(mappedTuple2._2()); // prints "2"
Tuple3 mappedTuple3 = tuple("foo", true, 1).biMap(x -> !x, x -> x + 1);
System.out.println(mappedTuple3._1()); // prints "foo"
System.out.println(mappedTuple3._2()); // prints false
System.out.println(mappedTuple3._3()); // prints 2
```
### Heterogeneous Maps
HMaps are type-safe heterogeneous maps, meaning they can store mappings to different value types in the same map; however, whereas HLists encode value types in their type signatures, HMaps rely on the keys to encode the value type that they point to.
```Java
TypeSafeKey stringKey = TypeSafeKey.typeSafeKey();
TypeSafeKey intKey = TypeSafeKey.typeSafeKey();
HMap hmap = HMap.hMap(stringKey, "string value",
intKey, 1);
Optional stringValue = hmap.get(stringKey); // Optional["string value"]
Optional intValue = hmap.get(intKey); // Optional[1]
Optional anotherIntValue = hmap.get(anotherIntKey); // Optional.empty
```
### CoProducts
`CoProduct`s generalize unions of disparate types in a single consolidated type, and the `ChoiceN` ADTs represent canonical implementations of these coproduct types.
```Java
CoProduct3 string = Choice3.a("string");
CoProduct3 integer = Choice3.b(1);
CoProduct3 character = Choice3.c('a');
```
Rather than supporting explicit value unwrapping, which would necessarily jeopardize type safety, `CoProduct`s support a `match` method that takes one function per possible value type and maps it to a final common result type:
```Java
CoProduct3 string = Choice3.a("string");
CoProduct3 integer = Choice3.b(1);
CoProduct3 character = Choice3.c('a');
Integer result = string.match(String::length, identity(), Character::charCount); // 6
```
Additionally, because a `CoProduct2` guarantees a subset of a `CoProduct3`, the `diverge` method exists between `CoProduct` types of single magnitude differences to make it easy to use a more convergent `CoProduct` where a more divergent `CoProduct` is expected:
```Java
CoProduct2 coProduct2 = Choice2.a("string");
CoProduct3 coProduct3 = coProduct2.diverge(); // still just the coProduct2 value, adapted to the coProduct3 shape
```
There are `CoProduct` and `Choice` specializations for type unions of up to 5 different types: `CoProduct2` through `CoProduct5`, and `Choice2` through `Choice5`, respectively.
### Either
`Either` represents a specialized `CoProduct2`, which resolve to one of two possible values: a left value wrapping an `L`, or a right value wrapping an `R` (typically an exceptional value or a successful value, respectively).
As with `CoProduct2`, rather than supporting explicit value unwrapping, `Either` supports many useful comprehensions to help facilitate type-safe interactions:
```Java
Either right = Either.right(1);
Either left = Either.left("Head fell off");
Integer result = right.orElse(-1);
//-> 1
List values = left.match(l -> Collections.emptyList(), Collections::singletonList);
//-> []
```
Check out the tests for [more examples](https://github.com/palatable/lambda/blob/master/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java) of ways to interact with `Either`.
Lenses
----
Lambda also ships with a first-class lens type, as well as a small library of useful general lenses:
```Java
Lens, List, Optional, String> stringAt0 = ListLens.at(0);
List strings = asList("foo", "bar", "baz");
view(stringAt0, strings); // Optional[foo]
set(stringAt0, "quux", strings); // [quux, bar, baz]
over(stringAt0, s -> s.map(String::toUpperCase).orElse(""), strings); // [FOO, bar, baz]
```
There are three functions that lambda provides that interface directly with lenses: `view`, `over`, and `set`. As the name implies, `view` and `set` are used to retrieve values and store values, respectively, whereas `over` is used to apply a function to the value a lens is focused on, alter it, and store it (you can think of `set` as a specialization of `over` using `constantly`).
Lenses can be easily created. Consider the following `Person` class:
```Java
public final class Person {
private final int age;
public Person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public Person setAge(int age) {
return new Person(age);
}
public Person setAge(LocalDate dob) {
return setAge((int) YEARS.between(dob, LocalDate.now()));
}
}
```
...and a lens for getting and setting `age` as an `int`:
```Java
Lens ageLensWithInt = Lens.lens(Person::getAge, Person::setAge);
//or, when each pair of type arguments match...
Lens.Simple alsoAgeLensWithInt = Lens.simpleLens(Person::getAge, Person::setAge);
```
If we wanted a lens for the `LocalDate` version of `setAge`, we could use the same method references and only alter the type signature:
```Java
Lens ageLensWithLocalDate = Lens.lens(Person::getAge, Person::setAge);
```
Compatible lenses can be trivially composed:
```Java
Lens, List, Optional, Integer> at0 = ListLens.at(0);
Lens