Skip to content

Instantly share code, notes, and snippets.

@calam1
Last active February 1, 2017 01:44
Show Gist options
  • Save calam1/0f8e5acc0b9df8bba03d393e49645b84 to your computer and use it in GitHub Desktop.
Save calam1/0f8e5acc0b9df8bba03d393e49645b84 to your computer and use it in GitHub Desktop.
/**
The following is not a java class, but some interfaces and implementations of Functors and Monads and a test class(Main.java)
FUNCTOR:
A functor is a typed data structure that encapsulates some value(s). Benefits of functors - they abstract away the internal representation and provide consistent, easy to use API over various data structures.
MONAD:
A monad a set of three things:
* a parameterized type M<T>
* a “unit” function T -> M<T>
* a “bind” operation: M<T> bind T -> M<U> = M<U>
i.e. the Optional monad:
Parameterized type: Optional<T>
unit: Optional.of()
bind: Optional.flatMap()
flatmap: flatMap() operator has to follow monad laws, but they are fairly intuitive like associativity of flatMap() and identity. The latter requires that m(x).flatMap(f) is the same as f(x) for any monad holding a value x and any function f.
i.e. - Optional<Integer> add(Optional<Integer> oa, Optional<Integer> ob) {
return oa.flatMap(a -> ob.map(b -> a + b));
}
Lambda inside flatMap has access to value of oa and uses it to increment value of ob similarly to previous inc function.
What if we need to perform other operations? Lets create another method that additionally accepts an operation
<A, B, R> Optional<R> compute(BiFunction<A, B, R> operation, Optional<A> oa, Optional<B> ob) {
return oa.flatMap(a -> ob.map(b -> operation.apply(a, b)));
}
credit to:
http://www.nurkiewicz.com/2016/06/functor-and-monad-examples-in-plain-java.html
https://github.com/earldouglas/java-scratchpad/tree/master/category-theory/monads
http://nazarii.bardiuk.com/java-monad/
*/
==========================================================================
import java.util.function.Function;
public interface Functor<T, F extends Functor<?, ?>> {
<R> F map(Function<T, R> f);
}
==========================================================================
import java.util.function.Function;
public interface Monad<T, M extends Monad<?, ?>> extends Functor<T, M> {
M flatMap(Function<T, M> f);
}
==========================================================================
import com.functional.Functor;
import java.util.function.Function;
public class FOptional<T> implements Functor<T, FOptional<?>> {
private final T valueOrNull;
public FOptional(T valueOrNull) {
this. valueOrNull = valueOrNull;
}
@Override
public <R> FOptional<R> map(Function<T, R> f) {
if (valueOrNull != null) {
return of(f.apply(valueOrNull));
} else {
return empty();
}
}
public static <T> FOptional<T> empty() {
return new FOptional<T>(null);
}
public static <T> FOptional<T> of(T value) {
return new FOptional<T>(value);
}
public static FOptional<Integer> tryParse(String s) {
try {
final int i = Integer.parseInt(s);
return FOptional.of(i);
} catch (NumberFormatException nfe) {
return FOptional.empty();
}
}
public static void main(String[] args) {
FOptional<String> str = FOptional.of("42");
FOptional<FOptional<Integer>> num = str.map(FOptional::tryParse);
System.out.println(num);
}
}
==========================================================================
import java.util.function.Function;
public class MOptional<T> implements Monad<T, MOptional<?>> {
final private T t;
public MOptional(T t) {
this.t = t;
}
@Override
public <R> MOptional<R> map(Function<T, R> f) {
if (t == null) {
return empty();
} else {
return of(f.apply(t));
}
}
public static <T> MOptional<T> of(T value) {
return new MOptional<>(value);
}
public static <T> MOptional<T> empty() {
return new MOptional<T>(null);
}
@Override
public <R> MOptional<R> flatMap(Function<T, MOptional<?>> f) {
if (t == null) {
return empty();
} else {
return (MOptional<R>) f.apply(t);
}
}
public static MOptional<Integer> tryParse(String s) {
try {
final int i = Integer.parseInt(s);
return MOptional.of(i);
} catch (NumberFormatException nfe) {
return MOptional.empty();
}
}
}
==========================================================================
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
public class Main {
public static Optional<Integer> add(Optional<Integer> oa, Optional<Integer> ob) {
return oa.flatMap(a -> ob.map(b -> a + b));
}
public static <A, B, R> Optional<R> compute(Optional<A> oa, Optional<B> ob, BiFunction<A, B, R> comp) {
return oa.flatMap(a -> ob.map(b -> comp.apply(a, b)));
}
private static class ZipCode {
public Optional<String> getLastFour() {
return lastFour;
}
final Optional<String> lastFour;
ZipCode(final String lastFour) {
this.lastFour = Optional.of(lastFour);
}
}
private static class Address {
public Optional<ZipCode> getZipCode() {
return zipCode;
}
final Optional<ZipCode> zipCode;
Address(final ZipCode zipCode) {
this.zipCode = Optional.of(zipCode);
}
}
private static class Employee {
public Optional<Address> getAddress() {
return address;
}
private final Optional<Address> address;
Employee(final Address address) {
this.address = Optional.of(address);
}
}
public static void main(String[] args) throws Exception {
System.out.println(
MOptional.of(1)
.map(x -> x + 20)
.map(x -> x * 2)
.flatMap(x -> {
if (x != 42) return MOptional.empty();
else return MOptional.of(x);
})
);
FOptional<String> test1 = FOptional.of("42");
// FOptional<Integer> test2 = test1.map(FOptional::tryParse); // this is an error the following happens
FOptional<FOptional<Integer>> test2 = test1.map(FOptional::tryParse);
FOptional<Integer> test22 = test1.map((x) -> Integer.parseInt(x));
FOptional<Integer> cc = FOptional.of("55")
.map((x) -> Integer.parseInt(x));
MOptional<String> test3 = MOptional.of("42");
// MOptional<MOptional<Integer>> test4 = test3.map(MOptional::tryParse); this has the same issue as a functor, nested result
MOptional<Integer> test4 = test3.flatMap(MOptional::tryParse); // this works
Optional<Integer> oa = Optional.of(3);
Optional<Integer> ob = Optional.of(6);
// Optional<Integer> ob = Optional.empty();
Optional<Integer> result = add(oa, ob);
if (result.isPresent()) {
System.out.println(result.get());
} else {
System.out.println("EMPTY");
}
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
Optional<Integer> addOpt = compute(oa, ob, add);
if (addOpt.isPresent()) {
System.out.println("compute add " + addOpt.get());
} else {
System.out.println("empty add " + Optional.empty());
}
Optional<Integer> multOpt = compute(oa, ob, multiply);
if (multOpt.isPresent()) {
System.out.println("compute mult " + multOpt.get());
} else {
System.out.println("empty mult " + Optional.empty());
}
List<Integer> integers = new ArrayList<>();
integers.add(5);
integers.add(6);
integers.add(7);
integers.stream().map(x -> new ArrayList<Integer>() {{add(x - 1); add(x); add(x + 1);}}).forEach(x -> System.out.println(x));
System.out.println("::::::::::::");
integers.stream().map(x -> new ArrayList<Integer>() {{add(x - 1); add(x); add(x + 1);}}).flatMap(x -> x.stream()).forEach(x -> System.out.println(x));
System.out.println("::::::::::::");
List<Integer> flat = integers.stream().map(x -> new ArrayList<Integer>() {{add(x - 1); add(x); add(x + 1);}}).flatMap(x -> x.stream()).sorted().collect(Collectors.toList());
System.out.println(flat);
System.out.println("::::::::::::");
List<List<Integer>> test = integers.stream().map(x -> new ArrayList<Integer>() {{add(x - 1); add(x); add(x + 1);}}).collect(Collectors.toList());
System.out.println(test);
Employee employee = new Employee(new Address(new ZipCode("1234")));
// Employee employee = new Employee(null);
Optional<Employee> employeeOptional = Optional.of(employee);
// Optional<String> zip = employeeOptional
// .map(e -> e.getAddress())
// .map(a -> a.getZipCode())
// .map(z -> z.getLastFour());
String zip = employeeOptional
.flatMap(Employee::getAddress)
.flatMap(Address::getZipCode)
.flatMap(ZipCode::getLastFour)
.orElseThrow(() -> new RuntimeException());
System.out.println(zip);
// if employee, address, and zip objects don't return Optional<> data then we use map, but we don't orElseThrow, we have Optional.empty();
// if (zip.isPresent()) {
// System.out.println("zipcode: " + zip.get());
// } else {
// System.out.println(Optional.empty());
// }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment