SlideShare a Scribd company logo
1 of 13
Download to read offline
Scala and Java Side by Side
The Result of Martin Fowler’s 1st Refactoring Example
Java’s records, sealed interfaces and text blocks are catching up with Scala’s case classes, sealed traits and multiline strings
Judge for yourself in this quick IDE-based visual comparison
of the Scala and Java translations of Martin Fowler’s refactored Javascript code
Martin Fowler
@martinfowler
@philip_schwarz
slides by https://www.slideshare.net/pjschwarz
@philip_schwarz
Java is in a constant process of catching up with some of the features found in other languages.
With this visual comparison of the Java and Scala translations of the refactored Javascript code from the 1st refactoring example of Martin Fowler’s
famous book, you can get some rapid and concrete evidence of some of the effects that Java’s evolution is having on programming in that language.
The code provides you with a simple example of the following Java features incorporated into long-term support (LTS) version JDK 17 (the previous
LTS version being JDK 11):
• Text blocks (JDK 15)
• Records (JDK 16)
• Sealed interfaces (JDK 17)
If you want to know how the Java and Scala translations of the Javascript code came about, see the following pair of slide decks and repositories
https://github.com/philipschwarz/refactoring-a-first-example-(java|scala)
https://www.slideshare.net/pjschwarz/refactoring-a-first-example-martin-fowlers-first-example-of-refactoring-adapted-to-(java|scala)
@main def main(): Unit =
assert(
statement(invoices(0), plays)
==
"""|Statement for BigCo
| Hamlet: $650.00 (55 seats)
| As You Like It: $580.00 (35 seats)
| Othello: $500.00 (40 seats)
|Amount owed is $1,730.00
|You earned 47 credits
|""".stripMargin
)
assert(
htmlStatement(invoices(0), plays)
==
"""|<h1>Statement for BigCo</h1>
|<table>
|<tr><th>play</th><th>seats</th><th>cost</th></tr>
|<tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr>
|<tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr>
|<tr><td>Othello</td><td>40</td><td>$500.00</td></tr>
|</table>
|<p>Amount owed is <em>$1,730.00</em></p>
|<p>You earned <em>47</em> credits</p>
|""".stripMargin
)
public static void main(String[] args) {
if (!Statement.statement(invoices.get(0), plays).equals(
"""
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
"""
)) throw new AssertionError();
if (!Statement.htmlStatement(invoices.get(0), plays).equals(
"""
<h1>Statement for BigCo</h1>
<table>
<tr><th>play</th><th>seats</th><th>cost</th></tr>
<tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr>
<tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr>
<tr><td>Othello</td><td>40</td><td>$500.00</td></tr>
</table>
<p>Amount owed is <em>$1,730.00</em></p>
<p>You earned <em>47</em> credits</p>
"""
)) throw new AssertionError();
}
static final List<Invoice> invoices =
List.of(
new Invoice(
"BigCo",
List.of(new Performance( "hamlet", 55),
new Performance("as-like", 35),
new Performance("othello", 40))));
static final Map<String,Play> plays = Map.of(
"hamlet" , new Play("Hamlet", "tragedy"),
"as-like", new Play("As You Like It", "comedy"),
"othello", new Play("Othello", "tragedy"));
val invoices: List[Invoice] = List(
Invoice( customer = "BigCo",
performances = List(Performance(playID = "hamlet", audience = 55),
Performance(playID = "as-like", audience = 35),
Performance(playID = "othello", audience = 40)))
)
val plays = Map (
"hamlet" -> Play(name = "Hamlet", `type` = "tragedy"),
"as-like" -> Play(name = "As You Like It", `type` = "comedy"),
"othello" -> Play(name = "Othello", `type` = "tragedy")
)
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
case class Play(
name: String,
`type`: String
)
case class Performance(
playID: String,
audience: Int
)
case class EnrichedPerformance(
playID: String,
play: Play,
audience: Int,
amount: Int,
volumeCredits: Int
)
case class Invoice(
customer: String,
performances: List[Performance]
)
case class StatementData(
customer: String,
performances: List[EnrichedPerformance],
totalAmount: Int,
totalVolumeCredits: Int
)
record Play(
String name,
String type
) { }
record Performance(
String playID,
int audience
) { }
record EnrichedPerformance(
String playID,
Play play,
int audience,
int amount,
int volumeCredits
) { }
record Invoice(
String customer,
List<Performance> performances
) { }
record StatementData(
String customer,
List<EnrichedPerformance> performances,
int totalAmount,
int totalVolumeCredits,
) { }
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
def renderPlainText(data: StatementData): String =
s"Statement for ${data.customer}n" + (
for
perf <- data.performances
yield s" ${perf.play.name}: ${usd(perf.amount/100)} (${perf.audience} seats)n"
).mkString +
s"""|Amount owed is ${usd(data.totalAmount/100)}
|You earned ${data.totalVolumeCredits} credits
|""".stripMargin
def renderHtml(data: StatementData): String =
s"""|<h1>Statement for ${data.customer}</h1>
|<table>
|<tr><th>play</th><th>seats</th><th>cost</th></tr>
|""".stripMargin + (
for
perf <- data.performances
yield s"<tr><td>${perf.play.name}</td><td>${perf.audience}</td>" +
s"<td>${usd(perf.amount/100)}</td></tr>n"
).mkString +
s"""|</table>
|<p>Amount owed is <em>${usd(data.totalAmount/100)}</em></p>
|<p>You earned <em>${data.totalVolumeCredits}</em> credits</p>
|""".stripMargin
static String renderPlainText(StatementData data) {
return
"Statement for %sn".formatted(data.customer()) +
data.performances()
.stream()
.map(p ->
" %s: %s (%d seats)n”
.formatted(p.play().name(), usd(p.amount()/100), p.audience())
).collect(Collectors.joining()) +
"""
Amount owed is %s
You earned %d credits
""".formatted(usd(data.totalAmount()/100), data.totalVolumeCredits());
}
static String renderHtml(StatementData data) {
return
"""
<h1>Statement for %s</h1>
<table>
<tr><th>play</th><th>seats</th><th>cost</th></tr>
""".formatted(data.customer()) +
data
.performances()
.stream()
.map(p -> "<tr><td>%s</td><td>%d</td><td>%s</td></tr>n"
.formatted(p.play().name(),p.audience(),usd(p.amount()/100))
).collect(Collectors.joining()) +
"""
</table>
<p>Amount owed is <em>%s</em></p>
<p>You earned <em>%d</em> credits</p>
""".formatted(usd(data.totalAmount()/100), data.totalVolumeCredits());
}
<h1>Statement for BigCo</h1>
<table>
<tr><th>play</th><th>seats</th><th>cost</th></tr>
<tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr>
<tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr>
<tr><td>Othello</td><td>40</td><td>$500.00</td></tr>
</table>
<p>Amount owed is <em>$1,730.00</em></p>
<p>You earned <em>47</em> credits</p>
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
public class Statement {
static String statement(Invoice invoice, Map<String, Play> plays) {
return renderPlainText(CreateStatementData.createStatementData(invoice,plays));
}
static String htmlStatement(Invoice invoice, Map<String, Play> plays) {
return renderHtml(CreateStatementData.createStatementData(invoice, plays));
}
static String usd(int aNumber) {
final var formatter = NumberFormat.getCurrencyInstance(Locale.US);
formatter.setCurrency(Currency.getInstance(Locale.US));
return formatter.format(aNumber);
}
…
def statement(invoice: Invoice, plays: Map[String, Play]): String =
renderPlainText(createStatementData(invoice,plays))
def htmlStatement(invoice: Invoice, plays: Map[String, Play]): String =
renderHtml(createStatementData(invoice,plays))
def usd(aNumber: Int): String =
val formatter = NumberFormat.getCurrencyInstance(Locale.US)
formatter.setCurrency(Currency.getInstance(Locale.US))
formatter.format(aNumber)
…
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
<h1>Statement for BigCo</h1>
<table>
<tr><th>play</th><th>seats</th><th>cost</th></tr>
<tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr>
<tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr>
<tr><td>Othello</td><td>40</td><td>$500.00</td></tr>
</table>
<p>Amount owed is <em>$1,730.00</em></p>
<p>You earned <em>47</em> credits</p>
public class CreateStatementData {
static StatementData createStatementData(Invoice invoice, Map<String, Play> plays) {
Function<Performance, Play> playFor =
aPerformance -> plays.get(aPerformance.playID());
Function<List<EnrichedPerformance>, Integer> totalVolumeCredits = (performances) ->
performances.stream().mapToInt(EnrichedPerformance::volumeCredits).sum();
Function<List<EnrichedPerformance>, Integer> totalAmount = (performances) ->
performances.stream().mapToInt(EnrichedPerformance::amount).sum();
…
def createStatementData(invoice: Invoice, plays: Map[String,Play]): StatementData =
def playFor(aPerformance: Performance): Play =
plays(aPerformance.playID)
def totalVolumeCredits(performances: List[EnrichedPerformance]): Int =
performances.map(_.volumeCredits).sum
def totalAmount(performances: List[EnrichedPerformance]): Int =
performances.map(_.amount).sum
…
def enrichPerformance(aPerformance: Performance): EnrichedPerformance =
val calculator =
PerformanceCalculator(aPerformance,playFor(aPerformance))
EnrichedPerformance(
aPerformance.playID,
calculator.play,
aPerformance.audience,
calculator.amount,
calculator.volumeCredits)p(_.amount).sum
val enrichedPerformances =
invoice.performances.map(enrichPerformance)
StatementData(
invoice.customer,
enrichedPerformances,
totalAmount(enrichedPerformances),
totalVolumeCredits(enrichedPerformances))
Function<Performance, EnrichedPerformance> enrichPerformance = aPerformance -> {
final var calculator =
PerformanceCalculator.instance(aPerformance,playFor.apply(aPerformance));
return new EnrichedPerformance(
aPerformance.playID(),
calculator.play(),
aPerformance.audience(),
calculator.amount(),
calculator.volumeCredits());
};
final var enrichedPerformances =
invoice.performances().stream().map(enrichPerformance).collect(toList());
return new StatementData(
invoice.customer(),
enrichedPerformances,
totalAmount.apply(enrichedPerformances),
totalVolumeCredits.apply(enrichedPerformances));
}
}
sealed trait PerformanceCalculator:
def performance: Performance
def play: Play
def amount: Int
def volumeCredits: Int = math.max(performance.audience - 30, 0)
object PerformanceCalculator:
def apply(aPerformance: Performance, aPlay: Play): PerformanceCalculator =
aPlay.`type` match
case "tragedy" => TragedyCalculator(aPerformance, aPlay)
case "comedy" => ComedyCalculator(aPerformance, aPlay)
case other => throw IllegalArgumentException(s"unknown type ${aPlay.`type`}")
…
sealed interface PerformanceCalculator {
Performance performance();
Play play();
int amount();
default int volumeCredits() { return Math.max(performance().audience() - 30, 0); }
static PerformanceCalculator instance(Performance aPerformance, Play aPlay) {
return switch (aPlay.type()) {
case "tragedy" -> new TragedyCalculator(aPerformance, aPlay);
case "comedy" -> new ComedyCalculator(aPerformance, aPlay);
default -> throw new IllegalArgumentException(String.format("unknown type '%s'", aPlay.type()));
};
}
}
…
case class TragedyCalculator(performance: Performance, play: Play) extends PerformanceCalculator:
def amount: Int =
val basicAmount = 40_000
val largeAudiencePremiumAmount =
if performance.audience <= 30 then 0 else 1_000 * (performance.audience - 30)
basicAmount + largeAudiencePremiumAmount
…
record TragedyCalculator(Performance performance, Play play) implements PerformanceCalculator {
@Override public int amount() {
final var basicAmount = 40_000;
final var largeAudiencePremiumAmount =
performance.audience() <= 30 ? 0 : 1_000 * (performance.audience() - 30);
return basicAmount + largeAudiencePremiumAmount;
}
}
…
case class ComedyCalculator(performance: Performance, play: Play) extends PerformanceCalculator:
def amount: Int =
val basicAmount = 30_000
val largeAudiencePremiumAmount =
if performance.audience <= 20 then 0 else 10_000 + 500 * (performance.audience - 20)
val audienceSizeAmount = 300 * performance.audience
basicAmount + largeAudiencePremiumAmount + audienceSizeAmount
override def volumeCredits: Int =
super.volumeCredits + math.floor(performance.audience / 5).toInt
record ComedyCalculator(Performance performance, Play play) implements PerformanceCalculator {
@Override public int amount() {
final var basicAmount = 30_000;
final var largeAudiencePremiumAmount =
performance.audience() <= 20 ? 0 : 10_000 + 500 * (performance.audience() - 20);
final var audienceSizeAmount = 300 * performance.audience();
return basicAmount + largeAudiencePremiumAmount + audienceSizeAmount;
}
@Override public int volumeCredits() {
return PerformanceCalculator.super.volumeCredits() + (int) Math.floor(performance().audience() / 5);
}
}
That’s all.
I hope you found it useful.

More Related Content

What's hot

Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free Monads
John De Goes
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
Mario Fusco
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The Wild
StackMob Inc
 

What's hot (20)

Testing in the World of Functional Programming
Testing in the World of Functional ProgrammingTesting in the World of Functional Programming
Testing in the World of Functional Programming
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free Monads
 
Advanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to FreeAdvanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to Free
 
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
 
Hammurabi
HammurabiHammurabi
Hammurabi
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The Wild
 
Lambdaconf2019 talk
Lambdaconf2019 talkLambdaconf2019 talk
Lambdaconf2019 talk
 
7 Habits For a More Functional Swift
7 Habits For a More Functional Swift7 Habits For a More Functional Swift
7 Habits For a More Functional Swift
 
Building a Functional Stream in Scala
Building a Functional Stream in ScalaBuilding a Functional Stream in Scala
Building a Functional Stream in Scala
 
Python : Dictionaries
Python : DictionariesPython : Dictionaries
Python : Dictionaries
 
Monoids, monoids, monoids
Monoids, monoids, monoidsMonoids, monoids, monoids
Monoids, monoids, monoids
 
Munihac 2018 - Beautiful Template Haskell
Munihac 2018 - Beautiful Template HaskellMunihac 2018 - Beautiful Template Haskell
Munihac 2018 - Beautiful Template Haskell
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & AnalyticsQuark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
 
The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210
 

Similar to Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refactoring Example

Os Fetterupdated
Os FetterupdatedOs Fetterupdated
Os Fetterupdated
oscon2007
 

Similar to Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refactoring Example (20)

Apache Velocity 1.6
Apache Velocity 1.6Apache Velocity 1.6
Apache Velocity 1.6
 
Lettering js
Lettering jsLettering js
Lettering js
 
Big Data Analytics with Scala at SCALA.IO 2013
Big Data Analytics with Scala at SCALA.IO 2013Big Data Analytics with Scala at SCALA.IO 2013
Big Data Analytics with Scala at SCALA.IO 2013
 
Idoc script beginner guide
Idoc script beginner guide Idoc script beginner guide
Idoc script beginner guide
 
vue-components.pdf
vue-components.pdfvue-components.pdf
vue-components.pdf
 
Beginning Scala Svcc 2009
Beginning Scala Svcc 2009Beginning Scala Svcc 2009
Beginning Scala Svcc 2009
 
Poetry with R -- Dissecting the code
Poetry with R -- Dissecting the codePoetry with R -- Dissecting the code
Poetry with R -- Dissecting the code
 
My First Rails Plugin - Usertext
My First Rails Plugin - UsertextMy First Rails Plugin - Usertext
My First Rails Plugin - Usertext
 
Lab final
Lab finalLab final
Lab final
 
Oscon 2010 Specs talk
Oscon 2010 Specs talkOscon 2010 Specs talk
Oscon 2010 Specs talk
 
Scala 3camp 2011
Scala   3camp 2011Scala   3camp 2011
Scala 3camp 2011
 
Table of Useful R commands.
Table of Useful R commands.Table of Useful R commands.
Table of Useful R commands.
 
Scala + WattzOn, sitting in a tree....
Scala + WattzOn, sitting in a tree....Scala + WattzOn, sitting in a tree....
Scala + WattzOn, sitting in a tree....
 
Contracts in-clojure-pete
Contracts in-clojure-peteContracts in-clojure-pete
Contracts in-clojure-pete
 
Scala 2 + 2 > 4
Scala 2 + 2 > 4Scala 2 + 2 > 4
Scala 2 + 2 > 4
 
How to Handle NoSQL with a Relational Database
How to Handle NoSQL with a Relational DatabaseHow to Handle NoSQL with a Relational Database
How to Handle NoSQL with a Relational Database
 
Os Fetterupdated
Os FetterupdatedOs Fetterupdated
Os Fetterupdated
 
Casting for not so strange actors
Casting for not so strange actorsCasting for not so strange actors
Casting for not so strange actors
 
Table and Form HTML&CSS
Table and Form HTML&CSSTable and Form HTML&CSS
Table and Form HTML&CSS
 
Php Sq Lite
Php Sq LitePhp Sq Lite
Php Sq Lite
 

More from Philip Schwarz

N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
Philip Schwarz
 

More from Philip Schwarz (20)

Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Folding Cheat Sheet #3 - third in a series
Folding Cheat Sheet #3 - third in a seriesFolding Cheat Sheet #3 - third in a series
Folding Cheat Sheet #3 - third in a series
 
Folding Cheat Sheet #2 - second in a series
Folding Cheat Sheet #2 - second in a seriesFolding Cheat Sheet #2 - second in a series
Folding Cheat Sheet #2 - second in a series
 
Folding Cheat Sheet #1 - first in a series
Folding Cheat Sheet #1 - first in a seriesFolding Cheat Sheet #1 - first in a series
Folding Cheat Sheet #1 - first in a series
 
Scala Left Fold Parallelisation - Three Approaches
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three Approaches
 
Tagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also Programs
 
Fusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
 
A sighting of traverse_ function in Practical FP in Scala
A sighting of traverse_ function in Practical FP in ScalaA sighting of traverse_ function in Practical FP in Scala
A sighting of traverse_ function in Practical FP in Scala
 
A sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in ScalaA sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in Scala
 
A sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in Scala
 
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Jordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axioms
 

Recently uploaded

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 

Recently uploaded (20)

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
What need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java DevelopersWhat need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java Developers
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
Crafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM IntegrationCrafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM Integration
 
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
 
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesGraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdfMicrosoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
 
IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion Production
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
how-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdfhow-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdf
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 

Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refactoring Example

  • 1. Scala and Java Side by Side The Result of Martin Fowler’s 1st Refactoring Example Java’s records, sealed interfaces and text blocks are catching up with Scala’s case classes, sealed traits and multiline strings Judge for yourself in this quick IDE-based visual comparison of the Scala and Java translations of Martin Fowler’s refactored Javascript code Martin Fowler @martinfowler @philip_schwarz slides by https://www.slideshare.net/pjschwarz
  • 2. @philip_schwarz Java is in a constant process of catching up with some of the features found in other languages. With this visual comparison of the Java and Scala translations of the refactored Javascript code from the 1st refactoring example of Martin Fowler’s famous book, you can get some rapid and concrete evidence of some of the effects that Java’s evolution is having on programming in that language. The code provides you with a simple example of the following Java features incorporated into long-term support (LTS) version JDK 17 (the previous LTS version being JDK 11): • Text blocks (JDK 15) • Records (JDK 16) • Sealed interfaces (JDK 17) If you want to know how the Java and Scala translations of the Javascript code came about, see the following pair of slide decks and repositories https://github.com/philipschwarz/refactoring-a-first-example-(java|scala) https://www.slideshare.net/pjschwarz/refactoring-a-first-example-martin-fowlers-first-example-of-refactoring-adapted-to-(java|scala)
  • 3. @main def main(): Unit = assert( statement(invoices(0), plays) == """|Statement for BigCo | Hamlet: $650.00 (55 seats) | As You Like It: $580.00 (35 seats) | Othello: $500.00 (40 seats) |Amount owed is $1,730.00 |You earned 47 credits |""".stripMargin ) assert( htmlStatement(invoices(0), plays) == """|<h1>Statement for BigCo</h1> |<table> |<tr><th>play</th><th>seats</th><th>cost</th></tr> |<tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr> |<tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr> |<tr><td>Othello</td><td>40</td><td>$500.00</td></tr> |</table> |<p>Amount owed is <em>$1,730.00</em></p> |<p>You earned <em>47</em> credits</p> |""".stripMargin ) public static void main(String[] args) { if (!Statement.statement(invoices.get(0), plays).equals( """ Statement for BigCo Hamlet: $650.00 (55 seats) As You Like It: $580.00 (35 seats) Othello: $500.00 (40 seats) Amount owed is $1,730.00 You earned 47 credits """ )) throw new AssertionError(); if (!Statement.htmlStatement(invoices.get(0), plays).equals( """ <h1>Statement for BigCo</h1> <table> <tr><th>play</th><th>seats</th><th>cost</th></tr> <tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr> <tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr> <tr><td>Othello</td><td>40</td><td>$500.00</td></tr> </table> <p>Amount owed is <em>$1,730.00</em></p> <p>You earned <em>47</em> credits</p> """ )) throw new AssertionError(); }
  • 4. static final List<Invoice> invoices = List.of( new Invoice( "BigCo", List.of(new Performance( "hamlet", 55), new Performance("as-like", 35), new Performance("othello", 40)))); static final Map<String,Play> plays = Map.of( "hamlet" , new Play("Hamlet", "tragedy"), "as-like", new Play("As You Like It", "comedy"), "othello", new Play("Othello", "tragedy")); val invoices: List[Invoice] = List( Invoice( customer = "BigCo", performances = List(Performance(playID = "hamlet", audience = 55), Performance(playID = "as-like", audience = 35), Performance(playID = "othello", audience = 40))) ) val plays = Map ( "hamlet" -> Play(name = "Hamlet", `type` = "tragedy"), "as-like" -> Play(name = "As You Like It", `type` = "comedy"), "othello" -> Play(name = "Othello", `type` = "tragedy") ) Statement for BigCo Hamlet: $650.00 (55 seats) As You Like It: $580.00 (35 seats) Othello: $500.00 (40 seats) Amount owed is $1,730.00 You earned 47 credits
  • 5. case class Play( name: String, `type`: String ) case class Performance( playID: String, audience: Int ) case class EnrichedPerformance( playID: String, play: Play, audience: Int, amount: Int, volumeCredits: Int ) case class Invoice( customer: String, performances: List[Performance] ) case class StatementData( customer: String, performances: List[EnrichedPerformance], totalAmount: Int, totalVolumeCredits: Int ) record Play( String name, String type ) { } record Performance( String playID, int audience ) { } record EnrichedPerformance( String playID, Play play, int audience, int amount, int volumeCredits ) { } record Invoice( String customer, List<Performance> performances ) { } record StatementData( String customer, List<EnrichedPerformance> performances, int totalAmount, int totalVolumeCredits, ) { } Statement for BigCo Hamlet: $650.00 (55 seats) As You Like It: $580.00 (35 seats) Othello: $500.00 (40 seats) Amount owed is $1,730.00 You earned 47 credits
  • 6. def renderPlainText(data: StatementData): String = s"Statement for ${data.customer}n" + ( for perf <- data.performances yield s" ${perf.play.name}: ${usd(perf.amount/100)} (${perf.audience} seats)n" ).mkString + s"""|Amount owed is ${usd(data.totalAmount/100)} |You earned ${data.totalVolumeCredits} credits |""".stripMargin def renderHtml(data: StatementData): String = s"""|<h1>Statement for ${data.customer}</h1> |<table> |<tr><th>play</th><th>seats</th><th>cost</th></tr> |""".stripMargin + ( for perf <- data.performances yield s"<tr><td>${perf.play.name}</td><td>${perf.audience}</td>" + s"<td>${usd(perf.amount/100)}</td></tr>n" ).mkString + s"""|</table> |<p>Amount owed is <em>${usd(data.totalAmount/100)}</em></p> |<p>You earned <em>${data.totalVolumeCredits}</em> credits</p> |""".stripMargin static String renderPlainText(StatementData data) { return "Statement for %sn".formatted(data.customer()) + data.performances() .stream() .map(p -> " %s: %s (%d seats)n” .formatted(p.play().name(), usd(p.amount()/100), p.audience()) ).collect(Collectors.joining()) + """ Amount owed is %s You earned %d credits """.formatted(usd(data.totalAmount()/100), data.totalVolumeCredits()); } static String renderHtml(StatementData data) { return """ <h1>Statement for %s</h1> <table> <tr><th>play</th><th>seats</th><th>cost</th></tr> """.formatted(data.customer()) + data .performances() .stream() .map(p -> "<tr><td>%s</td><td>%d</td><td>%s</td></tr>n" .formatted(p.play().name(),p.audience(),usd(p.amount()/100)) ).collect(Collectors.joining()) + """ </table> <p>Amount owed is <em>%s</em></p> <p>You earned <em>%d</em> credits</p> """.formatted(usd(data.totalAmount()/100), data.totalVolumeCredits()); } <h1>Statement for BigCo</h1> <table> <tr><th>play</th><th>seats</th><th>cost</th></tr> <tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr> <tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr> <tr><td>Othello</td><td>40</td><td>$500.00</td></tr> </table> <p>Amount owed is <em>$1,730.00</em></p> <p>You earned <em>47</em> credits</p> Statement for BigCo Hamlet: $650.00 (55 seats) As You Like It: $580.00 (35 seats) Othello: $500.00 (40 seats) Amount owed is $1,730.00 You earned 47 credits
  • 7. public class Statement { static String statement(Invoice invoice, Map<String, Play> plays) { return renderPlainText(CreateStatementData.createStatementData(invoice,plays)); } static String htmlStatement(Invoice invoice, Map<String, Play> plays) { return renderHtml(CreateStatementData.createStatementData(invoice, plays)); } static String usd(int aNumber) { final var formatter = NumberFormat.getCurrencyInstance(Locale.US); formatter.setCurrency(Currency.getInstance(Locale.US)); return formatter.format(aNumber); } … def statement(invoice: Invoice, plays: Map[String, Play]): String = renderPlainText(createStatementData(invoice,plays)) def htmlStatement(invoice: Invoice, plays: Map[String, Play]): String = renderHtml(createStatementData(invoice,plays)) def usd(aNumber: Int): String = val formatter = NumberFormat.getCurrencyInstance(Locale.US) formatter.setCurrency(Currency.getInstance(Locale.US)) formatter.format(aNumber) … Statement for BigCo Hamlet: $650.00 (55 seats) As You Like It: $580.00 (35 seats) Othello: $500.00 (40 seats) Amount owed is $1,730.00 You earned 47 credits <h1>Statement for BigCo</h1> <table> <tr><th>play</th><th>seats</th><th>cost</th></tr> <tr><td>Hamlet</td><td>55</td><td>$650.00</td></tr> <tr><td>As You Like It</td><td>35</td><td>$580.00</td></tr> <tr><td>Othello</td><td>40</td><td>$500.00</td></tr> </table> <p>Amount owed is <em>$1,730.00</em></p> <p>You earned <em>47</em> credits</p>
  • 8. public class CreateStatementData { static StatementData createStatementData(Invoice invoice, Map<String, Play> plays) { Function<Performance, Play> playFor = aPerformance -> plays.get(aPerformance.playID()); Function<List<EnrichedPerformance>, Integer> totalVolumeCredits = (performances) -> performances.stream().mapToInt(EnrichedPerformance::volumeCredits).sum(); Function<List<EnrichedPerformance>, Integer> totalAmount = (performances) -> performances.stream().mapToInt(EnrichedPerformance::amount).sum(); … def createStatementData(invoice: Invoice, plays: Map[String,Play]): StatementData = def playFor(aPerformance: Performance): Play = plays(aPerformance.playID) def totalVolumeCredits(performances: List[EnrichedPerformance]): Int = performances.map(_.volumeCredits).sum def totalAmount(performances: List[EnrichedPerformance]): Int = performances.map(_.amount).sum …
  • 9. def enrichPerformance(aPerformance: Performance): EnrichedPerformance = val calculator = PerformanceCalculator(aPerformance,playFor(aPerformance)) EnrichedPerformance( aPerformance.playID, calculator.play, aPerformance.audience, calculator.amount, calculator.volumeCredits)p(_.amount).sum val enrichedPerformances = invoice.performances.map(enrichPerformance) StatementData( invoice.customer, enrichedPerformances, totalAmount(enrichedPerformances), totalVolumeCredits(enrichedPerformances)) Function<Performance, EnrichedPerformance> enrichPerformance = aPerformance -> { final var calculator = PerformanceCalculator.instance(aPerformance,playFor.apply(aPerformance)); return new EnrichedPerformance( aPerformance.playID(), calculator.play(), aPerformance.audience(), calculator.amount(), calculator.volumeCredits()); }; final var enrichedPerformances = invoice.performances().stream().map(enrichPerformance).collect(toList()); return new StatementData( invoice.customer(), enrichedPerformances, totalAmount.apply(enrichedPerformances), totalVolumeCredits.apply(enrichedPerformances)); } }
  • 10. sealed trait PerformanceCalculator: def performance: Performance def play: Play def amount: Int def volumeCredits: Int = math.max(performance.audience - 30, 0) object PerformanceCalculator: def apply(aPerformance: Performance, aPlay: Play): PerformanceCalculator = aPlay.`type` match case "tragedy" => TragedyCalculator(aPerformance, aPlay) case "comedy" => ComedyCalculator(aPerformance, aPlay) case other => throw IllegalArgumentException(s"unknown type ${aPlay.`type`}") … sealed interface PerformanceCalculator { Performance performance(); Play play(); int amount(); default int volumeCredits() { return Math.max(performance().audience() - 30, 0); } static PerformanceCalculator instance(Performance aPerformance, Play aPlay) { return switch (aPlay.type()) { case "tragedy" -> new TragedyCalculator(aPerformance, aPlay); case "comedy" -> new ComedyCalculator(aPerformance, aPlay); default -> throw new IllegalArgumentException(String.format("unknown type '%s'", aPlay.type())); }; } } …
  • 11. case class TragedyCalculator(performance: Performance, play: Play) extends PerformanceCalculator: def amount: Int = val basicAmount = 40_000 val largeAudiencePremiumAmount = if performance.audience <= 30 then 0 else 1_000 * (performance.audience - 30) basicAmount + largeAudiencePremiumAmount … record TragedyCalculator(Performance performance, Play play) implements PerformanceCalculator { @Override public int amount() { final var basicAmount = 40_000; final var largeAudiencePremiumAmount = performance.audience() <= 30 ? 0 : 1_000 * (performance.audience() - 30); return basicAmount + largeAudiencePremiumAmount; } } …
  • 12. case class ComedyCalculator(performance: Performance, play: Play) extends PerformanceCalculator: def amount: Int = val basicAmount = 30_000 val largeAudiencePremiumAmount = if performance.audience <= 20 then 0 else 10_000 + 500 * (performance.audience - 20) val audienceSizeAmount = 300 * performance.audience basicAmount + largeAudiencePremiumAmount + audienceSizeAmount override def volumeCredits: Int = super.volumeCredits + math.floor(performance.audience / 5).toInt record ComedyCalculator(Performance performance, Play play) implements PerformanceCalculator { @Override public int amount() { final var basicAmount = 30_000; final var largeAudiencePremiumAmount = performance.audience() <= 20 ? 0 : 10_000 + 500 * (performance.audience() - 20); final var audienceSizeAmount = 300 * performance.audience(); return basicAmount + largeAudiencePremiumAmount + audienceSizeAmount; } @Override public int volumeCredits() { return PerformanceCalculator.super.volumeCredits() + (int) Math.floor(performance().audience() / 5); } }
  • 13. That’s all. I hope you found it useful.