Achieving FP in Java John Napier Achieving FP in Java John Napier - - PowerPoint PPT Presentation

achieving fp in java
SMART_READER_LITE
LIVE PREVIEW

Achieving FP in Java John Napier Achieving FP in Java John Napier - - PowerPoint PPT Presentation

Achieving FP in Java John Napier Achieving FP in Java John Napier Background Software developer at DRW, working with Trading Infrastructure teams Maintainer of lambda Trading Infrastructure teams Polyglot developers Ruby, C#,


slide-1
SLIDE 1

Achieving FP in Java

John Napier

slide-2
SLIDE 2
slide-3
SLIDE 3

Achieving FP in Java

John Napier

slide-4
SLIDE 4

Background

  • Software developer at DRW, working with Trading Infrastructure teams
  • Maintainer of lambda
slide-5
SLIDE 5

Trading Infrastructure teams

  • Polyglot developers
  • Ruby, C#, Clojure, Java
  • ~30 Java applications built on lambda
slide-6
SLIDE 6

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-7
SLIDE 7

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-8
SLIDE 8

boolean exists(String id);

slide-9
SLIDE 9

boolean exists(UUID id);

slide-10
SLIDE 10

List<Integer> numericParts(Float f);

slide-11
SLIDE 11

Tuple2<Integer, Integer> numericParts(Float f);

slide-12
SLIDE 12

public void process(List<Serializable> items) { for (Serializable item : items) { Integer value; if (item instanceof String) { value = ((String) item).length(); } else if (item instanceof Integer) { value = (Integer) item; } else { throw new IllegalArgumentException("Only allows strings and ints"); } // ../ } }

slide-13
SLIDE 13

public void process(List<CoProduct2<String, Integer, ? items) { for (CoProduct2<String, Integer, ?> item : items) { Integer value = item.match(Stringlength, integer  integer); // ../ } }

slide-14
SLIDE 14

Tuple2<Maybe<Error>, Maybe<Payload parseInput(String input);

slide-15
SLIDE 15

Either<Error, Payload> parseInput(String input);

slide-16
SLIDE 16

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-17
SLIDE 17

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-18
SLIDE 18

Function<String, Integer> function = Stringlength; Optional<Integer> optional = Optional.of(1); Stream<Integer> stream = Stream.of(1, 2, 3); CompletableFuture<Integer> future = CompletableFuture.completedFuture(1);

slide-19
SLIDE 19

Function<String, Integer> function = Stringlength; Optional<Integer> optional = Optional.of(1); Stream<Integer> stream = Stream.of(1, 2, 3); CompletableFuture<Integer> future = CompletableFuture.completedFuture(1); Function<Integer, Float> plusOneToFloat = x  x + 1F;

slide-20
SLIDE 20

Function<String, Integer> function = Stringlength; Optional<Integer> optional = Optional.of(1); Stream<Integer> stream = Stream.of(1, 2, 3); CompletableFuture<Integer> future = CompletableFuture.completedFuture(1); Function<Integer, Float> plusOneToFloat = x  x + 1F; Function<String, Float> mappedFunction = function.andThen(plusOneToFloat); Optional<Float> mappedOptional = optional.map(plusOneToFloat); Stream<Float> mappedStream = stream.map(plusOneToFloat); CompletableFuture<Float> mappedFuture = future.thenApply(plusOneToFloat);

slide-21
SLIDE 21

Function<String, Integer> function = Stringlength; Optional<Integer> optional = Optional.of(1); Stream<Integer> stream = Stream.of(1, 2, 3); CompletableFuture<Integer> future = CompletableFuture.completedFuture(1); Function<Integer, Float> plusOneToFloat = x  x + 1F; Function<String, Float> mappedFunction = function.andThen(plusOneToFloat); Optional<Float> mappedOptional = optional.map(plusOneToFloat); Stream<Float> mappedStream = stream.map(plusOneToFloat); CompletableFuture<Float> mappedFuture = future.thenApply(plusOneToFloat);

X

slide-22
SLIDE 22

public interface Functor<A, F extends Functor<?, F { <B> Functor<B, F> fmap(Function<? super A, ? extends B> fn); } class ImportJob<Result> implements Functor<Result, ImportJob<? { @Override public <B> ImportJob<B> fmap(Function<? super Result, ? extends B> fn) { // ../ } } class ResponseEnvelope<Body> implements Functor<Body, ResponseEnvelope<? { @Override public <B> ResponseEnvelope<B> fmap(Function<? super Body, ? extends B> fn) { // ../ } }

slide-23
SLIDE 23

ResponseEnvelope<String> envelope = /*../*/; ResponseEnvelope<Integer> mappedEnvelope = envelope.fmap(Stringlength); ImportJob<Integer> job = /*../*/; ImportJob<Float> mappedJob = job.fmap(IntegerfloatValue);

slide-24
SLIDE 24

Function<String, Integer> function = Stringlength; Optional<Integer> optional = Optional.of(1); Stream<Integer> stream = Stream.of(1, 2, 3); CompletableFuture<Integer> future = CompletableFuture.completedFuture(1); // Not on Function, but it's easily doable! Optional<Float> flatMappedOptional = optional .flatMap(x  x % 2  0 ? Optional.of(x / 2F) : Optional.empty()); Stream<Float> flatMappedStream = stream .flatMap(x  x % 2  0 ? Stream.of(x / 2F) : Stream.empty()); CompletableFuture<Float> flatMappedFuture = future .thenCompose(x  x % 2  0 ? completedFuture(x / 2F) : new CompletableFuture<Float>() {{ completeExceptionally(new IllegalStateException("oops")); }});

slide-25
SLIDE 25

public interface Monad<A, M extends Monad<?, M> extends Applicative<A, M> { // ../ <B> Monad<B, M> flatMap(Function<? super A, ? extends Monad<B, M f); } Iterable<Either<String, Integer eithers = asList(right(1), right(2), right(3)); // Right [1, 2, 3] Either<String, Iterable<Integer alsoFlipped = sequence(eithers, Eitherright); Iterable<Maybe<Integer maybes = asList(just(1), just(2), just(3)); // Just [1, 2, 3] Maybe<Iterable<Integer flipped = sequence(maybes, Maybejust); Maybe<Integer> maybe = just(1); Maybe<Float> mappedMaybe = just(1) .flatMap(x  x % 2  0 ? just(x / 2F) : nothing());

slide-26
SLIDE 26

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-27
SLIDE 27

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-28
SLIDE 28

public static List<LocalDate> daysBetween(LocalDate start, LocalDate end) { List<LocalDate> dates = new ArrayList(); LocalDate current = start; while (!end.isBefore(current)) { dates.add(current); current = current.plusDays(1); } return dates; } // later../ List<LocalDate> whatWeWant = lotsOfDays.subList(0, 10); LocalDate today = LocalDate.now(); LocalDate distantFuture = LocalDate.of(3000, 12, 25); List<LocalDate> lotsOfDays = daysBetween(today, distantFuture);

slide-29
SLIDE 29

public static Iterable<LocalDate> daysBetween(LocalDate start, LocalDate end) { return takeWhile(lt(end), iterate(current  current.plusDays(1), start)); } List<LocalDate> onHeap = toCollection(ArrayListnew, whatWeWant); // later../ Iterable<LocalDate> whatWeWant = take(10, lotsOfDays); // only ever computed 10 LocalDate today = LocalDate.now(); LocalDate distantFuture = LocalDate.of(3000, 12, 25); Iterable<LocalDate> lotsOfDays = daysBetween(today, distantFuture);

slide-30
SLIDE 30

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-31
SLIDE 31

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-32
SLIDE 32

public static Integer parseInt(String input) { // ../ }

slide-33
SLIDE 33

public static Maybe<Integer> parseInt(String input) { // ../ }

slide-34
SLIDE 34

class FixMessage { // ../ public static FixMessage parse(String input) { // ../ } } String input = "8=FIX.4.4|9=126|35=A|49=theBroker.12…"; FixMessage message = FixMessage.parse(input); FixMessage kaboom = FixMessage.parse("malformed");

slide-35
SLIDE 35

class FixMessage { // ../ public static Either<Errors, FixMessage> parse(String input) { // ../ } } String input = "8=FIX.4.4|9=126|35=A|49=theBroker.12…"; Either<Errors, FixMessage> message = FixMessage.parse(input); Either<Errors, FixMessage> whew = FixMessage.parse("malformed");

slide-36
SLIDE 36

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-37
SLIDE 37

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-38
SLIDE 38

public static Iterable<String> readTenLongestWords(Path path) throws IOException { return Map.<String, Iterable<Stringmap(line  asList(line.split("W+"))) .fmap(flatten()) .fmap(map(StringtoLowerCase)) .fmap(distinct()) .fmap(sortWith(comparing(Stringlength).reversed())) .fmap(take(10)) .apply(Files.readAllLines(path)); } // side-effect mixed with pure operation Iterable<String> words = readTenLongestWords(Paths.get("/tmp/two_cities.txt"));

slide-39
SLIDE 39

public static Iterable<String> tenLongestWords(Iterable<String> lines) { return Map.<String, Iterable<Stringmap(line  asList(line.split("W+"))) .fmap(flatten()) .fmap(map(StringtoLowerCase)) .fmap(distinct()) .fmap(sortWith(comparing(Stringlength).reversed())) .fmap(take(10)) .apply(lines); } public static IO<List<String readLines(Path path) { return io(checked(()  Files.readAllLines(path))); } IO<Iterable<String wordsIO = readLines(Paths.get("/tmp/two_cities.txt")) .fmap(SandboxtenLongestWords); // at the end of the world../ Iterable<String> words = wordsIO.unsafePerformIO();

slide-40
SLIDE 40

Iterable<Path> paths = asList(Paths.get("/tmp/two_cities.txt"), Paths.get("/tmp/sun_also_rises.txt")); Iterable<IO<List<String readAllFiles = map(SandboxreadLines, paths); IO<Iterable<String parallelizableIO = sequence(readAllFiles, IOio) .fmap(flatten()) .fmap(SandboxtenLongestWords); // at the end of the world, parallelized../ CompletableFuture<Iterable<String words = parallelizableIO.unsafePerformAsyncIO();

slide-41
SLIDE 41

Guiding Principles

  • Constraints should be precisely stated via types
  • Generic operations should have generic interfaces
  • Lazy evaluation is a useful default
  • Partial operations should be encoded as total operations
  • Pure and impure operations should be separate
slide-42
SLIDE 42

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-43
SLIDE 43

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-44
SLIDE 44

public interface Functor<A, F extends Functor<?, F { /* … */ } public interface Applicative<A, App extends Applicative<?, App extends Functor<A, App> { /* … */ } public interface Monad<A, M extends Monad<?, M extends Applicative<A, M> { /* … */ } public interface Traversable<A, T extends Traversable<?, T extends Functor<A, App> { /* … */ } public interface Contravariant<A, C extends Contravariant<?, T { /* … */ } public interface Profunctor<A, B, PF extends Profunctor<?, ?, PF extends Contravariant<A, Profunctor<?, B, PF { /* … */ }

slide-45
SLIDE 45

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-46
SLIDE 46

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-47
SLIDE 47

Maybe<Integer> maybe = just(1); Either<String, Boolean> right = right(true); Try<Exception, Float> try_ = trying(()  1F); Choice2<String, Integer> choice = a("string"); These<String, Integer> these = both("string", 1); Unit unit = UNIT;

slide-48
SLIDE 48

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-49
SLIDE 49

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-50
SLIDE 50

Fn2<Integer, Integer, Integer> add = Integersum; Fn1<Integer, Fn1<Integer, Integer alsoAdd = add; Fn1<Integer, Integer> add1 = add.apply(1); Integer sum = add.apply(1, 2); Fn0<Integer> deferredSum = add1.thunk(2);

slide-51
SLIDE 51

Semigroup<Integer> sumSg = Integersum; Integer foldedSum = sumSg.foldLeft(0, asList(1, 2, 3)); Monoid<Integer> sumM = monoid(sumSg, 0); Integer reducedSum = sumM.reduceLeft(asList(1, 2, 3)); Integer sumLengths = sumM.foldMap(Stringlength, asList("foo", "bar", "baz"));

slide-52
SLIDE 52

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-53
SLIDE 53

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-54
SLIDE 54

Iterable<Integer> nats = iterate(nat  nat + 1, 0); Iterable<Integer> evens = filter(x  x % 2  0, nats); Iterable<Integer> evenSquares = map(x  x * x, evens); Iterable<Integer> firstHundred = take(100, evenSquares); Iterable<Integer> greaterThan2500 = dropWhile(lte(2500), firstHundred); Iterable<Iterable<Integer groupedInTens = inGroupsOf(10, greaterThan2500); Iterable<Integer> sums = map(foldLeft(Integersum, 0), groupedInTens);

slide-55
SLIDE 55

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-56
SLIDE 56

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-57
SLIDE 57

HList.HNil nil = HList.nil(); SingletonHList<String> singleton = singletonHList("singleton"); SingletonHList<String> alsoSingleton = nil.cons("also singleton"); Tuple3<Float, String, Integer> tuple3 = nil.cons(1).cons("two").cons(3f); Tuple4<Byte, Short, Integer, Long> tuple4 = tuple((byte) 1, (short) 2, 3, 4L); Integer integer = tuple4._3(); Tuple3<Short, Integer, Long> tail = tuple4.tail();

slide-58
SLIDE 58

HMap emptyHMap = HMap.emptyHMap(); TypeSafeKey.Simple<String> fooKey = typeSafeKey(); TypeSafeKey.Simple<Integer> barKey = typeSafeKey(); HMap updated = emptyHMap .put(fooKey, "string") .put(barKey, 1); boolean containsBar = updated.containsKey(barKey); Maybe<Integer> maybeBar = updated.get(barKey); String fooOrError = updated.demand(fooKey);

slide-59
SLIDE 59

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-60
SLIDE 60

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-61
SLIDE 61

Lens.Simple<List<Integer>, Maybe<Integer firstElement = elementAt(0); Fn1<List<Integer>, Maybe<Integer viewFn = view(firstElement); Maybe<Integer> empty = view(firstElement, emptyList()); Maybe<Integer> just1 = view(firstElement, asList(1, 2, 3)); List<Integer> updated = over(firstElement, maybeX  maybeX.fmap(x  x + 1), asList(1, 2, 3)); List<Integer> updatedDifferently = set(firstElement, just(1), emptyList());

slide-62
SLIDE 62

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-63
SLIDE 63

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-64
SLIDE 64

public static IO<Unit> threadLog(Object message) { return io(()  System.out.println(currentThread().getName() + ": " + message)); } IO<Unit> helloWorld = threadLog("hello, “) .flatMap(constantly(threadLog(“world!"))); Unit unit = helloWorld.unsafePerformIO();

slide-65
SLIDE 65

public static IO<Unit> threadLog(Object message) { return io(()  System.out.println(currentThread().getName() + ": " + message)); } IO<Unit> helloWorld = threadLog("hello, “) .flatMap(constantly(threadLog(“world!"))); CompletableFuture<Unit> runningFuture = helloWorld.unsafePerformAsyncIO();

slide-66
SLIDE 66

Lambda Offerings

  • A model for functors, applicative functors, monads, and more
  • Common algebraic data types like Maybe and Either
  • Curried functions with specializations like Semigroup and Monoid
  • A rich library of functional iteration patterns like map and filter
  • Type-safe heterogeneous data structures like HList and HMap
  • Profunctor optics like Lens and Iso
  • An IO monad
slide-67
SLIDE 67

Demo

slide-68
SLIDE 68

λ

https://github.com/palatable/lambda https://gitter.im/palatable/lambda

slide-69
SLIDE 69