λ
======
[](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)
- [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.5.4
```
`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)):
```gradle
compile group: 'com.jnape.palatable', name: 'lambda', version: '1.5.4'
```
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(10, 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.
ADTs
----
In addition to the functions above, 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.
```Java
CoProduct3 string = CoProduct3.a("string");
CoProduct3 integer = CoProduct3.b(1);
CoProduct3 character = CoProduct3.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 = CoProduct3.a("string");
CoProduct3 integer = CoProduct3.b(1);
CoProduct3 character = CoProduct3.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 = CoProduct2.a("string");
CoProduct3 coProduct3 = coProduct2.diverge(); // still just the coProduct2 value, adapted to the coProduct3 shape
```
There are `CoProduct` specializations for type unions of up to 5 different types: `CoProduct2` through `CoProduct5`, 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