KEMBAR78
Poly-paradigm Java | PPTX
Poly-paradigm Java
Speaker
@pavletko
pav@p12v.comPavel Tcholakov
Poly-what?
Programming styles
• Imperative
• Object-oriented
• Aspect-oriented
• Functional
• Concurrent
• Actor
• Message-passing
• Logic, or rule-based
Lambda expressions
(Pojo p) -> {
return p.getProperty();
}
Streams
IntStream.range(0, 100)
.filter(i -> i % 2 == 0)
.map(i -> i * i)
.sum();
Longest string’s length
public int longestStringLength(List<String> strings) {
return strings.stream()
.mapToInt(String::length)
.max()
.getAsInt();
}
Longest string’s length
public String longestString(List<String> strings) {
return strings.stream()
.mapToInt(String::length)
.max()
// Ehh, now what???
}
Longest string’s length
public String longestString(List<String> strings) {
final String[] maxString = {null};
strings.stream()
.forEach(s -> {
if (s.length() > maxString[0].length()) {
maxString[0] = s;
}
});
return maxString[0];
}
Longest string
public String longestString(List<String> strings) {
return strings.stream()
.reduce((s1, s2) ->
s1.length() > s2.length() ? s1 : s2)
.get();
}
Longest string
public String longestString(List<String> strings) {
return strings.stream()
.max(Comparator.comparingInt(s -> s.length()))
.get();
}
Streams and functions
public List<String> findAllPalindromes(List<String> strings) {
return strings.stream()
.filter(s -> Objects.equals(s, StringUtils.reverse(s)))
.collect(Collectors.toList());
}
Functions, continued
public List<String> findAllPalindromes(List<String> strings) {
return strings.stream()
.filter(s -> Objects.equals(s, StringUtils.reverse(s)))
.collect(Collectors.toList());
}
Higher order function
public List<String> findUnchangedStrings(
List<String> strings,
Function<String, String> transformation) {
return strings.stream()
.filter(s -> Objects.equals(s, transformation.apply(s)))
.collect(Collectors.toList());
}
Higher order, continued
public List<String> palindromes(List<String> strings) {
return findUnchangedThings(strings, StringUtils::reverse);
}
public List<String> findShouting(List<String> strings) {
return findUnchangedThings(strings, StringUtils::capitalize);
}
Generic enough?
public List<String> findUnchangedStrings(
List<String> strings,
Function<String, String> transformation) {
return strings.stream()
.filter(s -> Objects.equals(s, transformation.apply(s)))
.collect(Collectors.toList());
}
Type parameters
public <T> List<T> findUnchangedThings(
List<? extends T> things,
Function<? super T, ? extends T> transformation) {
return things.stream()
.filter(t -> Objects.equals(t, transformation.apply(t)))
.collect(Collectors.toList());
}
public List<String> palindromes(List<String> strings) {
return findUnchangedThings(strings, StringUtils::reverse);
}
public List<String> findShouting(List<String> strings) {
return findUnchangedThings(strings, StringUtils::capitalize);
}
public List<Integer> nonNegative(List<Integer> ints) {
return findUnchangedThings(ints, Math::abs);
}
public <T> List<T> findUnchangedThings(
List<? extends T> things,
Function<? super T, ? extends T> transformation) {
return things.stream()
.filter(t -> Objects.equals(t, transformation.apply(t)))
.collect(Collectors.toList());
}
Monads?
Internal DSLs
Vagrant.configure("2") do |config|
config.vm.box = "hashicorp/precise64"
config.vm.provision :shell, path: "bootstrap.sh"
config.vm.network :forwarded_port, guest: 80, host: 4567
end
JavaSlang pattern matching
Stream.of(0, 1, 2, 3, 13, 14, null, -1)
.peek(n -> out.print(format("%d -> ", n)))
.map(Match.as(Object.class)
.when(Objects::isNull).then("!")
.whenIs(0).then("zero")
.whenIsIn(1, 13, 14).then(i -> "first digit 1: " + i)
.whenType(Double.class).then(d -> "Found a double: " + d)
.whenApplicable((Number num) -> "number: " + num).thenApply()
.otherwise(() -> "no match"))
.map(Object::toString)
Actor model
Concurrency-oriented
programming
• We identify all the truly concurrent activities in our
real world activity.
• We identify all message channels between the
concurrent activities.
• We write down all the messages which can flow on
the different message channels.
— Joe Armstrong
Akka actor in Java
public class Greeter extends AbstractActor {
String greeting = "";
public Greeter() {
receive(ReceiveBuilder.
match(WhoToGreet.class,
message -> greeting = "hello, " + message.who).
match(Greet.class,
message -> sender()
.tell(new Greeting(greeting), self())).
build());
}
}
Logic programming
Prolog
fact.
another_fact.
jug(jozi_jug).
jug(virtual_jug).
java_meetup(java_day).
attendee(pavel, java_day).
attendee(nitsan, java_day).
attendee(richard, linux_meetup).
interested(java, Who) :-
attendee(Who, Meetup),
java_meetup(Meetup).
Demo
N-queens in Prolog
solution([]).
solution([X/Y|Others]) :-
solution(Others),
member(Y, [1,2,3,4,5,6,7,8]),
noattack(X/Y, Others).
noattack(_, []).
noattack(X/Y, [X1/Y1|Others]) :-
Y == Y1,
Y1 - Y == X1 - X,
Y1 - Y == X - X1,
noattack(X/Y,Others).
Sudoku in Clojure core.logic
(defn sudokufd [hints]
(let [vars (repeatedly 81 lvar)
rows (->> vars (partition 9) (map vec) (into []))
cols (apply map vector rows)
sqs (for [x (range 0 9 3)
y (range 0 9 3)]
(get-square rows x y))]
(run 1 [q]
(== q vars)
(everyg #(fd/in % (fd/domain 1 2 3 4 5 6 7 8 9)) vars)
(init vars hints)
(everyg fd/distinct rows)
(everyg fd/distinct cols)
(everyg fd/distinct sqs))))
Ok, how about some Java?
Rule-driven DCFT pattern
public interface Task {
boolean isInGoalState();
void handleEvents(Set<Event> events);
void applyRules(Consumer<Event> bus);
}
public boolean isInGoalState() {
return state == MemberState.FOLLOWER &&
lastHeartbeat.plus(ELECTION_TIMEOUT).isAfter(clock.now());
}
public void handleEvents(Set<Event> events) {
for (Event evt : events) {
if (evt instanceof AppendEntriesRpcReceived) {
lastHeartbeat = clock.now();
}
}
}
public void applyRules(Consumer<Event> eventBus) {
if (state == MemberState.FOLLOWER) {
if (lastHeartbeat.plus(ELECTION_TIMEOUT).isAfter(clock.now())) {
state = MemberState.CANDIDATE;
eventBus.accept(new MemberStateChange(state));
eventBus.accept(new LeaderVote(serverId));
} else {
// keep calm and carry on
}
} else {
// nothing to do in other states
}
}
Causes of complexity
• State
• Control
• Code volume
• Others: complexity breeds complexity
FRP architecture
• Essential State
➡ A Relational definition of the stateful components of the
system
• Essential Logic
➡ Derived-relation definitions, integrity constraints and (pure)
functions
• Accidental State and Control
➡ A declarative specification of a set of performance
optimisations for the system
for (int i = 1; i <= 100; i++) {
if (i % 15 == 0) {
out.println("FizzBuzz");
} else if (i % 3 == 0) {
out.println("Fizz");
} else if (i % 5 == 0) {
out.println("Buzz");
} else {
out.println(i);
}
}
for (int i = 1; i <= 100; i++) {
if (i % 15 == 0) {
out.println("FizzBuzz");
} else if (i % 3 == 0) {
out.println("Fizz");
} else if (i % 5 == 0) {
out.println("Buzz");
} else {
out.println(i);
}
}
static Predicate<Integer> divisibleBy(Integer div) {
return (i) -> i % div == 0;
}
class Replacement {
final Predicate<Integer> when; final String output;
public Replacement(Predicate<Integer> when, String output) {
this.when = when; this.output = output;
}
}
List<Replacement> fizzAndOrBuzz =
Collections.unmodifiableList(Arrays.asList(
new Replacement(divisibleBy(3), "Fizz"),
new Replacement(divisibleBy(5), "Buzz")));
static String replace(Integer i, List<Replacement> rules) {
return rules.stream()
.filter(replacement -> replacement.when.test(i))
.map(replacement -> replacement.output)
.reduce(String::concat)
.orElse(i.toString());
}
static String replace(Integer i, List<Replacement> rules) {
return rules.stream()
.filter(replacement -> replacement.when.test(i))
.map(replacement -> replacement.output)
.reduce(String::concat)
.orElse(i.toString());
}
static String replace(Integer i, List<Replacement> rules) {
Stream<String> applicableReplacements = rules.stream()
.filter(replacement -> replacement.when.test(i))
.map(replacement -> replacement.output);
return applicableReplacements
.reduce(String::concat)
.orElse(i.toString());
}
static String fizzBuzz(Integer i) {
return replace(i, fizzAndOrBuzz);
}
// …
IntStream.rangeClosed(1, 100)
.mapToObj(FizzBuzz::fizzBuzz)
.forEach(out::println);
static Stream<String> fizzBuzz(IntStream intStream) {
return intStream.mapToObj(FizzBuzz::fizzBuzz);
}
// …
IntStream infiniteInts = IntStream.iterate(1, i -> i + 1);
fizzBuzz(infiniteInts)
.limit(100)
.forEach(out::println);
public interface FizzBuzz {
class Replacement { final Predicate<Integer> when; final String output; … }
static Predicate<Integer> divisibleBy(Integer d) { return i -> i % d == 0; }
List<Replacement> fizzBuzzRules = unmodifiableList(asList(
new Replacement(divisibleBy(3), "Fizz"),
new Replacement(divisibleBy(5), "Buzz")));
static String replace(Integer i, List<Replacement> rules) {
return rules.stream()
.filter(replacement -> replacement.when.test(i))
.map(replacement -> replacement.output)
.reduce(String::concat)
.orElse(i.toString());
}
static void main(String... args) {
IntStream.rangeClosed(1, 100)
.mapToObj(i -> replace(i, fizzBuzzRules))
.forEach(out::println);
}
}
Performance
Benchmark Mode Cnt Score Error Units
FizzBenchmark.functionalSolution avgt 5 13.122 ± 4.030 us/op
FizzBenchmark.functionalSolution2 avgt 5 16.922 ± 2.558 us/op
FizzBenchmark.imperativeSolution avgt 5 1.579 ± 0.142 us/op
2015 MacBook Pro 15”, i7 2.2GHz
Conclusions
Questions?
Feedback
@pavletko
pav@p12v.comPavel Tcholakov
“Simplicity can only be attained if it is
recognised, sought and prized.”
— Excerpt From: Ben Moseley, “Out of the Tar Pit.”
Thank you!

Poly-paradigm Java

  • 1.
  • 2.
  • 3.
  • 4.
    Programming styles • Imperative •Object-oriented • Aspect-oriented • Functional • Concurrent • Actor • Message-passing • Logic, or rule-based
  • 6.
    Lambda expressions (Pojo p)-> { return p.getProperty(); }
  • 8.
    Streams IntStream.range(0, 100) .filter(i ->i % 2 == 0) .map(i -> i * i) .sum();
  • 9.
    Longest string’s length publicint longestStringLength(List<String> strings) { return strings.stream() .mapToInt(String::length) .max() .getAsInt(); }
  • 10.
    Longest string’s length publicString longestString(List<String> strings) { return strings.stream() .mapToInt(String::length) .max() // Ehh, now what??? }
  • 11.
    Longest string’s length publicString longestString(List<String> strings) { final String[] maxString = {null}; strings.stream() .forEach(s -> { if (s.length() > maxString[0].length()) { maxString[0] = s; } }); return maxString[0]; }
  • 12.
    Longest string public StringlongestString(List<String> strings) { return strings.stream() .reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2) .get(); }
  • 13.
    Longest string public StringlongestString(List<String> strings) { return strings.stream() .max(Comparator.comparingInt(s -> s.length())) .get(); }
  • 14.
    Streams and functions publicList<String> findAllPalindromes(List<String> strings) { return strings.stream() .filter(s -> Objects.equals(s, StringUtils.reverse(s))) .collect(Collectors.toList()); }
  • 15.
    Functions, continued public List<String>findAllPalindromes(List<String> strings) { return strings.stream() .filter(s -> Objects.equals(s, StringUtils.reverse(s))) .collect(Collectors.toList()); }
  • 16.
    Higher order function publicList<String> findUnchangedStrings( List<String> strings, Function<String, String> transformation) { return strings.stream() .filter(s -> Objects.equals(s, transformation.apply(s))) .collect(Collectors.toList()); }
  • 17.
    Higher order, continued publicList<String> palindromes(List<String> strings) { return findUnchangedThings(strings, StringUtils::reverse); } public List<String> findShouting(List<String> strings) { return findUnchangedThings(strings, StringUtils::capitalize); }
  • 18.
    Generic enough? public List<String>findUnchangedStrings( List<String> strings, Function<String, String> transformation) { return strings.stream() .filter(s -> Objects.equals(s, transformation.apply(s))) .collect(Collectors.toList()); }
  • 19.
    Type parameters public <T>List<T> findUnchangedThings( List<? extends T> things, Function<? super T, ? extends T> transformation) { return things.stream() .filter(t -> Objects.equals(t, transformation.apply(t))) .collect(Collectors.toList()); }
  • 20.
    public List<String> palindromes(List<String>strings) { return findUnchangedThings(strings, StringUtils::reverse); } public List<String> findShouting(List<String> strings) { return findUnchangedThings(strings, StringUtils::capitalize); } public List<Integer> nonNegative(List<Integer> ints) { return findUnchangedThings(ints, Math::abs); } public <T> List<T> findUnchangedThings( List<? extends T> things, Function<? super T, ? extends T> transformation) { return things.stream() .filter(t -> Objects.equals(t, transformation.apply(t))) .collect(Collectors.toList()); }
  • 21.
  • 30.
    Internal DSLs Vagrant.configure("2") do|config| config.vm.box = "hashicorp/precise64" config.vm.provision :shell, path: "bootstrap.sh" config.vm.network :forwarded_port, guest: 80, host: 4567 end
  • 31.
    JavaSlang pattern matching Stream.of(0,1, 2, 3, 13, 14, null, -1) .peek(n -> out.print(format("%d -> ", n))) .map(Match.as(Object.class) .when(Objects::isNull).then("!") .whenIs(0).then("zero") .whenIsIn(1, 13, 14).then(i -> "first digit 1: " + i) .whenType(Double.class).then(d -> "Found a double: " + d) .whenApplicable((Number num) -> "number: " + num).thenApply() .otherwise(() -> "no match")) .map(Object::toString)
  • 32.
  • 33.
    Concurrency-oriented programming • We identifyall the truly concurrent activities in our real world activity. • We identify all message channels between the concurrent activities. • We write down all the messages which can flow on the different message channels. — Joe Armstrong
  • 34.
    Akka actor inJava public class Greeter extends AbstractActor { String greeting = ""; public Greeter() { receive(ReceiveBuilder. match(WhoToGreet.class, message -> greeting = "hello, " + message.who). match(Greet.class, message -> sender() .tell(new Greeting(greeting), self())). build()); } }
  • 35.
  • 36.
  • 38.
  • 39.
    N-queens in Prolog solution([]). solution([X/Y|Others]):- solution(Others), member(Y, [1,2,3,4,5,6,7,8]), noattack(X/Y, Others). noattack(_, []). noattack(X/Y, [X1/Y1|Others]) :- Y == Y1, Y1 - Y == X1 - X, Y1 - Y == X - X1, noattack(X/Y,Others).
  • 40.
    Sudoku in Clojurecore.logic (defn sudokufd [hints] (let [vars (repeatedly 81 lvar) rows (->> vars (partition 9) (map vec) (into [])) cols (apply map vector rows) sqs (for [x (range 0 9 3) y (range 0 9 3)] (get-square rows x y))] (run 1 [q] (== q vars) (everyg #(fd/in % (fd/domain 1 2 3 4 5 6 7 8 9)) vars) (init vars hints) (everyg fd/distinct rows) (everyg fd/distinct cols) (everyg fd/distinct sqs))))
  • 41.
    Ok, how aboutsome Java?
  • 43.
  • 46.
    public interface Task{ boolean isInGoalState(); void handleEvents(Set<Event> events); void applyRules(Consumer<Event> bus); }
  • 47.
    public boolean isInGoalState(){ return state == MemberState.FOLLOWER && lastHeartbeat.plus(ELECTION_TIMEOUT).isAfter(clock.now()); } public void handleEvents(Set<Event> events) { for (Event evt : events) { if (evt instanceof AppendEntriesRpcReceived) { lastHeartbeat = clock.now(); } } } public void applyRules(Consumer<Event> eventBus) { if (state == MemberState.FOLLOWER) { if (lastHeartbeat.plus(ELECTION_TIMEOUT).isAfter(clock.now())) { state = MemberState.CANDIDATE; eventBus.accept(new MemberStateChange(state)); eventBus.accept(new LeaderVote(serverId)); } else { // keep calm and carry on } } else { // nothing to do in other states } }
  • 49.
    Causes of complexity •State • Control • Code volume • Others: complexity breeds complexity
  • 50.
    FRP architecture • EssentialState ➡ A Relational definition of the stateful components of the system • Essential Logic ➡ Derived-relation definitions, integrity constraints and (pure) functions • Accidental State and Control ➡ A declarative specification of a set of performance optimisations for the system
  • 52.
    for (int i= 1; i <= 100; i++) { if (i % 15 == 0) { out.println("FizzBuzz"); } else if (i % 3 == 0) { out.println("Fizz"); } else if (i % 5 == 0) { out.println("Buzz"); } else { out.println(i); } }
  • 53.
    for (int i= 1; i <= 100; i++) { if (i % 15 == 0) { out.println("FizzBuzz"); } else if (i % 3 == 0) { out.println("Fizz"); } else if (i % 5 == 0) { out.println("Buzz"); } else { out.println(i); } }
  • 54.
    static Predicate<Integer> divisibleBy(Integerdiv) { return (i) -> i % div == 0; }
  • 55.
    class Replacement { finalPredicate<Integer> when; final String output; public Replacement(Predicate<Integer> when, String output) { this.when = when; this.output = output; } } List<Replacement> fizzAndOrBuzz = Collections.unmodifiableList(Arrays.asList( new Replacement(divisibleBy(3), "Fizz"), new Replacement(divisibleBy(5), "Buzz")));
  • 56.
    static String replace(Integeri, List<Replacement> rules) { return rules.stream() .filter(replacement -> replacement.when.test(i)) .map(replacement -> replacement.output) .reduce(String::concat) .orElse(i.toString()); }
  • 57.
    static String replace(Integeri, List<Replacement> rules) { return rules.stream() .filter(replacement -> replacement.when.test(i)) .map(replacement -> replacement.output) .reduce(String::concat) .orElse(i.toString()); }
  • 58.
    static String replace(Integeri, List<Replacement> rules) { Stream<String> applicableReplacements = rules.stream() .filter(replacement -> replacement.when.test(i)) .map(replacement -> replacement.output); return applicableReplacements .reduce(String::concat) .orElse(i.toString()); }
  • 59.
    static String fizzBuzz(Integeri) { return replace(i, fizzAndOrBuzz); } // … IntStream.rangeClosed(1, 100) .mapToObj(FizzBuzz::fizzBuzz) .forEach(out::println);
  • 60.
    static Stream<String> fizzBuzz(IntStreamintStream) { return intStream.mapToObj(FizzBuzz::fizzBuzz); } // … IntStream infiniteInts = IntStream.iterate(1, i -> i + 1); fizzBuzz(infiniteInts) .limit(100) .forEach(out::println);
  • 61.
    public interface FizzBuzz{ class Replacement { final Predicate<Integer> when; final String output; … } static Predicate<Integer> divisibleBy(Integer d) { return i -> i % d == 0; } List<Replacement> fizzBuzzRules = unmodifiableList(asList( new Replacement(divisibleBy(3), "Fizz"), new Replacement(divisibleBy(5), "Buzz"))); static String replace(Integer i, List<Replacement> rules) { return rules.stream() .filter(replacement -> replacement.when.test(i)) .map(replacement -> replacement.output) .reduce(String::concat) .orElse(i.toString()); } static void main(String... args) { IntStream.rangeClosed(1, 100) .mapToObj(i -> replace(i, fizzBuzzRules)) .forEach(out::println); } }
  • 62.
    Performance Benchmark Mode CntScore Error Units FizzBenchmark.functionalSolution avgt 5 13.122 ± 4.030 us/op FizzBenchmark.functionalSolution2 avgt 5 16.922 ± 2.558 us/op FizzBenchmark.imperativeSolution avgt 5 1.579 ± 0.142 us/op 2015 MacBook Pro 15”, i7 2.2GHz
  • 64.
  • 65.
  • 66.
  • 67.
    “Simplicity can onlybe attained if it is recognised, sought and prized.” — Excerpt From: Ben Moseley, “Out of the Tar Pit.”
  • 68.

Editor's Notes

  • #2 Thanks for coming! Thanks Oracle and day’s sponsors for bringing the Java community together.
  • #3 Engineer @ AWS Cape Town (opinions my own) Current job: operate a moderately successful cloud service, we run many EC2 control plane services from CT office Past: Sys architect @ Discovery; financial services, web, mobile, systems integration, network systems, security builder Java tinkerer for many years: encountered java in ~99-2k; prod since 2003 Also: Groovy, Scala, Clojure (<3), & others (Ruby) Multiple Java 8 applications in production
  • #4 Polyglot -> poly-paradigm. By analogy, instead of picking up French, we could learn more idioms in English. Perhaps by studying other approaches and languages we could pick up some useful tricks. Design patterns have perhaps fallen off the radar a bit since the horrors of EJB 2 - time to reconsider? Human performance aspect
  • #5 AOP: Who’s using Java EE and/or Spring Framework?
  • #6 FP building systems out of functions treating functions as a first-class citizen in the language design pure functions have no side effects Anyone using: Scala / Groovy?
  • #7 Simple syntax for creating instances of functional interfaces. Pass around like any other value. How do we use them?
  • #8 Introduce some FP concepts with the help of Streams, new in Java 8 Who’s on Java 8?
  • #9 Streams looks neat! how nifty is this example?!
  • #10 We can do more complicated things, still easy!
  • #11 Ehh?
  • #12 Bad! Don’t do this. Side effects outside streams can lead to unpredictable results! Also, if we switched to parallel stream this will produce incorrect results.
  • #13 Note the .get() call - we have an Optional result.
  • #14 Turns out Streams authors thought of this!
  • #16 Extract function parameter.
  • #17 Ta-da!
  • #18 Same shape problem, different function parameters give us different behavior
  • #20 Introduced type parameter. Still same shape problem, now reusable with different data types.
  • #21 Reuse through composition. FP = higher-order thinking!
  • #22 Anyone using pure FP languages such as Haskell, ML, OCaml, F#?
  • #24 Monad: just a design pattern Used to construct computations by chaining pure functions It helps provide some context to the functions; basically a box
  • #26 what if the domain of the function is a box?
  • #27 flatMap to the rescue! unpacks the box before passing on
  • #30 Optional, Stream, CompletableFuture
  • #31 Brief aside: Lambdas make internal DSLs way nicer. Anyone seen a Vagrant config file? It’s just Ruby
  • #32 Pattern matching: a language feature in e.g. Scala, implemented as a DSL. Fluent builder API with heavy reliance on lambda.
  • #35 Characterised by: many, many concurrent actors. Pushing the limits of JVM - new lightweight threading models needed to scale to millions of actors per JVM; fast concurrent primitives like JCTools super important building blocks.
  • #36 Who has worked with Prolog or other constraint programming environment?
  • #37 Atoms: facts; facts with variables. Rules: used to infer new facts and answer queries.
  • #38 Unification: start with DB of facts; look at rules with unbound variables - generate something valid in place of a variable; does it unify with existing facts? yes -> keep going; no -> backtrack.
  • #42 Demo of Java / core.logic Tinkering with Raft. Hand rolled Actors but encountered difficulties dealing with handling all the states.
  • #43 Came across this paper… –– Tinkering with Raft. Hand rolled Actors but encountered difficulties dealing with handling all the states.
  • #44 A task consists of three elements: a collection of state variables, a set of rules, and a goal. We did not consciously choose a rules-based approach for RAMCloud’s DCFT modules: the code gradually as- sumed this form as we added functionality over a series of refactorings. We use the term rules-based to describe a style of pro- gramming where there is not a crisp algorithm that pro- gresses monotonically from beginning to end.
  • #45 It’s an actor! Spot the state monad
  • #46 It’s a state machine! The state machine definition is broad enough that it in- cludes the rules-based approach as a special case. However, in most state machines the actions are determined directly from the events. Rules use a two-step approach where event handlers only modify state and never take actions. Rules then trigger based on the resulting state, which reflects the sum of the outside events. The difference between these two approaches is subtle, but the rules-based approach results in a cleaner code factoring.
  • #48 Can you spot the state dependency that’s crept in?
  • #49 All this made me think of an older paper, OOTTP by Moseley and Marks
  • #50 “Avoid accidental state and complexity whenever you can, and if you can’t, then separate it from the rest of the system.”
  • #51 The logic component determines the meaning, whereas the control component only affects efficiency. I.e. we should still get the correct results even if we remove any accidental control (think removing a hot index from a DB table) Separate all three; avoid useless accidental state & control
  • #52 “We have argued that complexity causes more problems in large software systems than anything else. We have also argued that it can be tamed — but only through a concerted effort to avoid it where possible, and to separate it where not. Specifically we have argued that a system can usefully be separated into three main parts: the essential state, the essential logic, and the accidental state and control.” — Separate all three; avoid useless accidental state & control — Frege (islands of functional purity), relational/logic programming languages — The logic component determines the meaning, whereas the control component only affects efficiency. I.e. we should still get the correct results even if we remove any accidental control (think removing a hot index from a DB table)
  • #53 Check how we’re doing for time first! Skip this section if running late. Anybody recognise this? That’s right, FizzBuzz!
  • #55 Start by defining a factory method that spits out Predicate instances, using a Lambda expression. Predicate is a functional interface defined in Java 8 which is a function from something to a boolean.
  • #56 Next, we’ll continue to model our domain: “Replacement” groups a predicate that matches something, and the appropriate alternative string.
  • #58 We could break up this long chaining for better understanding.
  • #60 Now we can finally define a fizzBuzz helper that turns an integer into a string, using the rules we’ve defined. Applying it to a stream of ints is trivial.
  • #61 Another possibility: return a brand new stream transformed with our replacement rule. We can apply it to infinite streams! Works because of laziness.
  • #62 Upside: no dependency injection or AbstractFactoryFactories in sight! Downside: ~10x slower Decision: human costs, is this any easier to read / understand / maintain?
  • #64 Excellent rant, keynote by Joe Armstrong @ StrangeLoop 2014 5.2 32-bit words have as many possible states as all the atoms of the Earth! Our systems have enormous state spaces; not always necessary Who can program without Google & StackOverflow? We’ve made a mess and we need to reverse entropy in order to fix it
  • #65 Learning new languages is great, exposure to new paradigms. Read academic papers! Think about design - and design patterns. More constrained tools sometimes force creativity by enforcing simplicity. (Prime lens photography) State is bad. Manage the combinatorial explosion of state, compounded by explicit control. Eliminate accidental complexity wherever you recognise it. Don’t optimise prematurely. Is multi-paradigm truly possible? In Java? (Scala?) Is it a good idea? Even if it isn't, some of these ideas at least give us a framework for decomposing polyglot systems. Java is and will remain blue-collar programming language. Apply polyglot judiciously (e.g. Frege). “Simplicity can only be attained if it is recognised, sought and prized.” — Excerpt From: Ben Moseley. “Out of the Tar Pit.” Have fun!
  • #67 Feedback is the breakfast of champions!
  • #69 And our host and sponsors!