Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Haskell, Scala and Java

Philip Schwarz
Philip SchwarzSoftware development is what I am passionate about
Sum and Product Types
The Fruit Salad & Fruit Snack Example
From F# to Haskell, Scala and Java
@ScottWlaschin
type FruitSalad = {
Apple: AppleVariety
Banana: BananaVariety
Cherries: CherryVariety
}
In F#, new types are built from smaller types in two ways:
• By _AND_ing them together
• By _OR_ing them together
“AND” Types
Let’s start with building types using AND. For example, we might say that to
make fruit salad you need an apple and a banana and some cherries:
“OR” Types
The other way of building new types is by using OR. For example, we might
say that for a fruit snack you need an apple or a banana or some cherries:
@ScottWlaschin
type FruitSnack =
| Apple of AppleVariety
| Banana of BananaVariety
| Cherries of CherryVariety
The varieties of fruit are themselves defined as OR types, which in this case is used
similarly to an enum in other languages.
type AppleVariety =
| GoldenDelicious
| GrannySmith
| Fuji
type BananaVariety =
| Cavendish
| GrosMichel
| Manzano
type CherryVariety =
| Montmorency
| Bing
This can be read as:
• An AppleVariety is either a GoldenDelicious or a GrannySmith or a Fuji, and so on.
@ScottWlaschin
Jargon Alert: “Product Types” and “Sum Types”
The types that are built using AND are called product types.
The types that are built using OR are called sum types or tagged unions or, in F#
terminology, discriminated unions. In this book I will often call them choice types,
because I think that best describes their role in domain modeling.
@philip_schwarz
Let’s translate that F# example into Haskell, Scala and Java.
data FruitSalad = FruitSalad {
apple :: AppleVariety,
banana :: BananaVariety,
cherries :: CherryVariety
}
data FruitSnack
= Apple AppleVariety
| Banana BananaVariety
| Cherries CherryVariety
data AppleVariety
= GoldenDelicious
| GrannySmith
| Fuji
data BananaVariety
= Cavendish
| GrosMichel
| Manzano
data CherryVariety
= Montmorency
| Bing
case class FruitSalad(
apple: AppleVariety,
banana: BananaVariety,
cherries: CherryVariety
)
enum FruitSnack:
case Apple(variety: AppleVariety)
case Banana(variety: BananaVariety)
case Cherries(variety: CherryVariety)
enum AppleVariety:
case GoldenDelicious,
GrannySmith,
Fuji
enum BananaVariety:
case Cavendish,
GrosMichel,
Manzano
enum CherryVariety:
case Montmorency,
Bing
record FruitSalad(
AppleVariety apple,
BananaVariety banana,
CherryVariety cherries
) { }
sealed interface FruitSnack permits Apple, Banana, Cherries { }
record Apple(AppleVariety variety) implements FruitSnack { }
record Banana(BananaVariety variety) implements FruitSnack { }
record Cherries(CherryVariety variety) implements FruitSnack { }
enum AppleVariety {
GOLDEN_DELICIOUS,
GRANNY_SMITH,
FUJI}
enum BananaVariety {
CAVENDISH,
GROS_MICHEL,
MANZANO}
enum CherryVariety {
MONTMORENCY,
BING}
Now, let’s see what the behaviour is when we compare
sample values and when we convert them to strings.
data FruitSalad = FruitSalad {
apple :: AppleVariety,
banana :: BananaVariety,
cherries :: CherryVariety
} deriving (Eq, Show)
data FruitSnack
= Apple AppleVariety
| Banana BananaVariety
| Cherries CherryVariety
deriving (Eq, Show)
data AppleVariety
= GoldenDelicious
| GrannySmith
| Fuji
deriving (Eq, Show)
data BananaVariety
= Cavendish
| GrosMichel
| Manzano
deriving (Eq, Show)
data CherryVariety
= Montmorency
| Bing
deriving (Eq, Show)
main :: IO ()
main =
let
salad = FruitSalad GoldenDelicious Cavendish Montmorency
sameSalad = FruitSalad GoldenDelicious Cavendish Montmorency
differentSalad = FruitSalad GoldenDelicious Manzano Montmorency
snack = Apple GoldenDelicious
sameSnack = Apple GoldenDelicious
differentSnack = Banana Cavendish
in do
assert (show salad == "FruitSalad {apple = GoldenDelicious, banana = Cavendish, cherries = Montmorency}") pure ()
assert (salad == sameSalad) return ()
assert (salad /= differentSalad) return ()
assert (show snack == "Apple GoldenDelicious") return ()
assert (snack == sameSnack) return ()
assert (snack /= differentSnack) return ()
-- Error: Couldn't match expected type ‘FruitSalad’ with actual type ‘FruitSnack’
assert(snack /= salad) return ()
-- Error: Couldn't match expected type ‘FruitSnack’ with actual type ‘AppleVariety’
assert(snack /= GoldenDelicious) return ()
-- Error: Couldn't match expected type ‘FruitSnack’ with actual type ‘AppleVariety’
assert(salad /= GoldenDelicious) return ()
To permit the ‘showing’ of FruitSalad and
FruitSnack values, and also to permit the
comparison of such values, we have added the
following to all types: deriving (Eq, Show).
val salad = FruitSalad(GoldenDelicious, Cavendish, Montmorency);
val sameSalad = FruitSalad(GoldenDelicious, Cavendish, Montmorency);
val differentSalad = FruitSalad(GoldenDelicious, Manzano, Montmorency);
val snack = Apple(GoldenDelicious)
val sameSnack = Apple(GoldenDelicious)
val differentSnack = Banana(Cavendish)
assert(salad.toString == "FruitSalad(GoldenDelicious,Cavendish,Montmorency)")
assert(salad == sameSalad)
assert(salad != differentSalad)
assert(snack.toString == "Apple(GoldenDelicious)")
assert(snack == sameSnack);
assert(snack != differentSnack);
// Compiler error: Values of types FruitSalad and FruitSnack cannot be compared with == or !=
assert(salad != snack)
// Compiler error: Values of types FruitSalad and AppleVariety cannot be compared with == or !=
assert(salad != GoldenDelicious)
// Compiler error: Values of types FruitSnack and AppleVariety cannot be compared with == or !=
assert(snack != GoldenDelicious)
case class FruitSalad(
apple: AppleVariety,
banana: BananaVariety,
cherries: CherryVariety
) derives CanEqual
enum FruitSnack derives CanEqual:
case Apple(variety: AppleVariety)
case Banana(variety: BananaVariety)
case Cherries(variety: CherryVariety)
enum AppleVariety:
case GoldenDelicious,
GrannySmith,
Fuji
enum BananaVariety:
case Cavendish,
GrosMichel,
Manzano
enum CherryVariety:
case Montmorency,
Bing
To prevent meaningless comparisons, e.g.
comparing a salad with a snack, we have
added the following to FruitSalad and
FruitSnack: derives CanEqual.
case class FruitSalad(
apple: AppleVariety,
banana: BananaVariety,
cherries: CherryVariety
) derives CanEqual
enum FruitSnack derives CanEqual:
case Apple(variety: AppleVariety)
case Banana(variety: BananaVariety)
case Cherries(variety: CherryVariety)
enum AppleVariety:
case GoldenDelicious,
GrannySmith,
Fuji
enum BananaVariety:
case Cavendish,
GrosMichel,
Manzano
enum CherryVariety:
case Montmorency,
Bing
products (AND)
degenerate products - single argument
product (AND)
sum (OR)
record FruitSalad(
AppleVariety apple,
BananaVariety banana,
CherryVariety cherries
) { }
sealed interface FruitSnack permits Apple, Banana, Cherries { }
record Apple(AppleVariety variety) implements FruitSnack { }
record Banana(BananaVariety variety) implements FruitSnack { }
record Cherries(CherryVariety variety) implements FruitSnack { }
enum AppleVariety {
GOLDEN_DELICIOUS,
GRANNY_SMITH,
FUJI}
enum BananaVariety {
CAVENDISH,
GROS_MICHEL,
MANZANO}
enum CherryVariety {
MONTMORENCY,
BING}
var salad = new FruitSalad(GOLDEN_DELICIOUS,CAVENDISH, MONTMORENCY);
var sameSalad = new FruitSalad(GOLDEN_DELICIOUS,CAVENDISH, MONTMORENCY);
var differentSalad = new FruitSalad(GOLDEN_DELICIOUS,MANZANO, MONTMORENCY);
var snack = new Apple(GOLDEN_DELICIOUS);
var sameSnack = new Apple(GOLDEN_DELICIOUS);
var differentSnack = new Banana(CAVENDISH);
assert(salad.toString().equals("FruitSalad[apple=GOLDEN_DELICIOUS, banana=CAVENDISH, cherries=MONTMORENCY]"));
assert(salad.equals(sameSalad));
assert(!salad.equals(differentSalad));
assert(snack.toString().equals("Apple[variety=GOLDEN_DELICIOUS]"));
assert(snack.equals(sameSnack));
assert(!snack.equals(differentSnack));
assert(!salad.equals(snack));
assert(!salad.equals(GOLDEN_DELICIOUS));
assert(!snack.equals(GOLDEN_DELICIOUS));
Now, let’s see some pattern matching
@philip_schwarz
pickyCustomerReaction :: FruitSalad -> String
pickyCustomerReaction (FruitSalad Fuji Cavendish Bing) = "That's my favourite combination."
pickyCustomerReaction (FruitSalad GoldenDelicious _ _) = "I can't stand Golden Delicious apples."
pickyCustomerReaction (FruitSalad _ Manzano Bing) = "I both love and hate this."
pickyCustomerReaction (FruitSalad _ Manzano _) = "Manzano is my least favourite banana."
pickyCustomerReaction (FruitSalad _ _ Bing) = "Bing are my favourite cherries."
pickyCustomerReaction _ = "It will do."
data FruitSalad = FruitSalad {
apple :: AppleVariety,
banana :: BananaVariety,
cherries :: CherryVariety
} deriving (Eq, Show)
data FruitSnack
= Apple AppleVariety
| Banana BananaVariety
| Cherries CherryVariety
deriving (Eq, Show)
data AppleVariety
= GoldenDelicious
| GrannySmith
| Fuji
deriving (Eq, Show)
data BananaVariety
= Cavendish
| GrosMichel
| Manzano
deriving (Eq, Show)
data CherryVariety
= Montmorency
| Bing
deriving (Eq, Show)
pickySnackerRemark :: FruitSnack -> String
pickySnackerRemark (Apple Fuji) = "That's my favourite apple."
pickySnackerRemark (Apple GoldenDelicious) = "I can't stand Golden Delicious apples."
pickySnackerRemark (Banana Cavendish) = "That's my favourite banana."
pickySnackerRemark (Banana Manzano) = "Manzano is my least favourite banana."
pickySnackerRemark (Cherries Bing) = "Those are my favourite cherries."
pickySnackerRemark _ = "It will do."
case class FruitSalad(
apple: AppleVariety,
banana: BananaVariety,
cherries: CherryVariety
)
enum FruitSnack:
case Apple(variety: AppleVariety)
case Banana(variety: BananaVariety)
case Cherries(variety: CherryVariety)
enum AppleVariety:
case GoldenDelicious,
GrannySmith,
Fuji
enum BananaVariety:
case Cavendish,
GrosMichel,
Manzano
enum CherryVariety:
case Montmorency,
Bing
val pickyCustomerReaction = salad match
case FruitSalad(Fuji,Cavendish,Bing) => "That's my favourite combination."
case FruitSalad(GoldenDelicious,_,_) => "I can't stand Golden Delicious apples.”
case FruitSalad(_,Manzano,Bing) => "I both love and hate this."
case FruitSalad(_,Manzano,_) => "Manzano is my least favourite banana."
case FruitSalad(_,_,Bing) => "Bing are my favourite cherries."
case _ => "It will do."
val pickySnackerRemark = snack match
case Apple(Fuji) => "That's my favourite apple."
case Apple(GoldenDelicious) => "I can't stand Golden Delicious apples."
case Banana(Cavendish) => "That's my favourite banana."
case Banana(Manzano) => "Manzano is my least favourite banana."
case Cherries(Bing) => "Those are my favourite cherries."
case _ => "It will do."
record FruitSalad(
AppleVariety apple,
BananaVariety banana,
CherryVariety cherries
) { }
sealed interface FruitSnack permits Apple, Banana, Cherries { }
record Apple(AppleVariety variety) implements FruitSnack { }
record Banana(BananaVariety variety) implements FruitSnack { }
record Cherries(CherryVariety variety) implements FruitSnack { }
enum AppleVariety {
GOLDEN_DELICIOUS,
GRANNY_SMITH,
FUJI}
enum BananaVariety {
CAVENDISH,
GROS_MICHEL,
MANZANO}
enum CherryVariety {
MONTMORENCY,
BING}
String pickyCustomerReaction(FruitSalad salad) {
return switch (salad) {
case FruitSalad(var apple, var banana, var cherries)
when apple.equals(FUJI) && banana.equals(CAVENDISH)
&& cherries.equals(BING) ->
"That's my favourite combination.";
case FruitSalad(var apple, var banana, var cherries)
when apple.equals(GOLDEN_DELICIOUS) ->
"I can't stand Golden Delicious apples.";
case FruitSalad(var apple, var banana, var cherries)
when banana.equals(MANZANO) && cherries.equals(BING) ->
"I both love and hate this.";
case FruitSalad(var apple, var banana, var cherries)
when banana.equals(MANZANO) ->
"Manzano is my least favourite banana.";
case FruitSalad(var apple, var banana, var cherries)
when cherries.equals(BING) ->
"Bing are my favourite cherries.";
default -> "It will do.";
};
}
String pickySnackerRemark(FruitSnack snack) {
return switch (snack) {
case Apple(var variety) when variety.equals(FUJI) ->"That's my favourite apple.";
case Apple(var variety) when variety.equals(GOLDEN_DELICIOUS) ->"I can't stand Golden Delicious apples.";
case Banana(var variety) when variety.equals(CAVENDISH) ->"That's my favourite banana.";
case Banana(var variety) when variety.equals(MANZANO) ->"Manzano is my least favourite banana.";
case Cherries(var variety) when variety.equals(BING) ->"Those are my favourite cherries.";
default -> "It will do.";
};
}
In order to run that pattern matching code, I
downloaded the Java 19 early access build.
$ ~/Downloads/jdk-19.jdk/Contents/Home/bin/jshell --enable-preview
| Welcome to JShell -- Version 19-ea
| For an introduction type: /help intro
jshell> record FruitSalad(
...> AppleVariety apple,
...> BananaVariety banana,
...> CherryVariety cherries
...> ) { }
...>
...> sealed interface FruitSnack permits Apple, Banana, Cherries { }
...> record Apple(AppleVariety variety) implements FruitSnack { }
...> record Banana(BananaVariety variety) implements FruitSnack { }
...> record Cherries(CherryVariety variety) implements FruitSnack { }
...>
...> enum AppleVariety {GOLDEN_DELICIOUS, GRANNY_SMITH, FUJI}
...> enum BananaVariety {CAVENDISH, GROS_MICHEL, MANZANO}
...> enum CherryVariety {MONTMORENCY, BING}
| created record FruitSalad, however, it cannot be referenced until class AppleVariety, class BananaVariety, and class CherryVariety are declared
| created interface FruitSnack, however, it cannot be referenced until class Apple, class Banana, and class Cherries are declared
| created record Apple, however, it cannot be referenced until class AppleVariety is declared
| created record Banana, however, it cannot be referenced until class BananaVariety is declared
| created record Cherries, however, it cannot be referenced until class CherryVariety is declared
| created enum AppleVariety
| created enum BananaVariety
| created enum CherryVariety
jshell>
jshell> String pickySnackerRemark(FruitSnack snack) {
...> return switch (snack) {
...> case Apple(var variety) when variety.equals(AppleVariety.FUJI) ->"That's my favourite apple.";
...> case Apple(var variety) when variety.equals(AppleVariety.GOLDEN_DELICIOUS) ->"I can't stand Golden Delicious apples.";
...> case Banana(var variety) when variety.equals(BananaVariety.CAVENDISH) ->"That's my favourite banana.";
...> case Banana(var variety) when variety.equals(BananaVariety.MANZANO) ->"Manzano is my least favourite banana.";
...> case Cherries(var variety) when variety.equals(CherryVariety.BING) ->"Those are my favourite cherries.";
...> default -> "It will do.";
...> };
...> }
| created method pickySnackerRemark(FruitSnack)
jshell> FruitSnack snack = new Banana(BananaVariety.MANZANO);
snack ==> Banana[variety=MANZANO]
jshell> pickySnackerRemark(snack)
$11 ==> "Manzano is my least favourite banana.”
jshell>
jshell> String pickyCustomerReaction(FruitSalad salad) {
...> return switch (salad) {
...> case FruitSalad(var apple, var banana , var cherries)
...> when apple.equals(AppleVariety.FUJI) && banana.equals(BananaVariety.CAVENDISH) && cherries.equals(CherryVariety.BING) ->
...> "That's my favourite combination.";
...> case FruitSalad(var apple, var banana , var cherries)
...> when apple.equals(AppleVariety.GOLDEN_DELICIOUS) ->
...> "I can't stand Golden Delicious apples.";
...> case FruitSalad(var apple, var banana , var cherries)
...> when banana.equals(BananaVariety.MANZANO) ->
...> "Manzano is my least favourite banana.";
...> case FruitSalad(var apple, var banana , var cherries)
...> when cherries.equals(CherryVariety.BING) ->
...> "Bing are my favourite cherries.";
...> case FruitSalad(var apple, var banana , var cherries)
...> when banana.equals(BananaVariety.MANZANO) && cherries.equals(CherryVariety.BING) ->
...> "I both love and hate this.";
...> default -> "It will do.";
...> };
...> }
| created method pickyCustomerReaction(FruitSalad)
jshell> var salad = new FruitSalad(AppleVariety.GOLDEN_DELICIOUS,BananaVariety.CAVENDISH, CherryVariety.MONTMORENCY);
salad ==> FruitSalad[apple=GOLDEN_DELICIOUS, banana=CAVENDISH, cherries=MONTMORENCY]
jshell> pickyCustomerReaction(salad);
$14 ==> "I can't stand Golden Delicious apples."
jshell>
I had a go at applying those suggestions.
The first one was fine.
When I tried the second one, I ran into some issues.
If I find out more from Brian Goetz, or I manage to resolve
the issue, then I’ll publish a new version of this deck.
That’s all. I hope you found it useful.
@philip_schwarz
1 of 20

Recommended

Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t... by
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...Philip Schwarz
1.2K views46 slides
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1 by
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Philip Schwarz
1.9K views33 slides
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2 by
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Philip Schwarz
1.3K views77 slides
The aggregate function - from sequential and parallel folds to parallel aggre... by
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...Philip Schwarz
262 views31 slides
Domain Driven Design with the F# type System -- F#unctional Londoners 2014 by
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Scott Wlaschin
171.5K views117 slides
Http4s, Doobie and Circe: The Functional Web Stack by
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
6.3K views33 slides

More Related Content

What's hot

Railway Oriented Programming by
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
638.7K views154 slides
The Functional Programming Triad of Map, Filter and Fold by
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldPhilip Schwarz
3.5K views51 slides
Ad hoc Polymorphism using Type Classes and Cats by
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsPhilip Schwarz
1.5K views54 slides
Domain Modeling with FP (DDD Europe 2020) by
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Scott Wlaschin
1.5K views95 slides
Monoids - Part 1 - with examples using Scalaz and Cats by
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsPhilip Schwarz
1.7K views39 slides
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える by
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるpospome
65.3K views119 slides

What's hot(20)

Railway Oriented Programming by Scott Wlaschin
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
Scott Wlaschin638.7K views
The Functional Programming Triad of Map, Filter and Fold by Philip Schwarz
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and Fold
Philip Schwarz3.5K views
Ad hoc Polymorphism using Type Classes and Cats by Philip Schwarz
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and Cats
Philip Schwarz1.5K views
Domain Modeling with FP (DDD Europe 2020) by Scott Wlaschin
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)
Scott Wlaschin1.5K views
Monoids - Part 1 - with examples using Scalaz and Cats by Philip Schwarz
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
Philip Schwarz1.7K views
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える by pospome
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
pospome65.3K views
High-Performance Haskell by Johan Tibell
High-Performance HaskellHigh-Performance Haskell
High-Performance Haskell
Johan Tibell18.6K views
PHPUnit 4.0で追加されたwillReturn*()の紹介 by Takaaki Hirano
PHPUnit 4.0で追加されたwillReturn*()の紹介PHPUnit 4.0で追加されたwillReturn*()の紹介
PHPUnit 4.0で追加されたwillReturn*()の紹介
Takaaki Hirano795 views
Arriving at monads by going from pure-function composition to effectful-funct... by Philip Schwarz
Arriving at monads by going from pure-function composition to effectful-funct...Arriving at monads by going from pure-function composition to effectful-funct...
Arriving at monads by going from pure-function composition to effectful-funct...
Philip Schwarz922 views
Sequence and Traverse - Part 2 by Philip Schwarz
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
Philip Schwarz3.2K views
Java ORマッパー選定のポイント #jsug by Masatoshi Tada
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
Masatoshi Tada90K views
Domain Modeling Made Functional (KanDDDinsky 2019) by Scott Wlaschin
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)
Scott Wlaschin3K views
Domain Modeling in a Functional World by Debasish Ghosh
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
Debasish Ghosh50.6K views
オブジェクト指向できていますか? by Moriharu Ohzu
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
Moriharu Ohzu237.5K views
The lazy programmer's guide to writing thousands of tests by Scott Wlaschin
The lazy programmer's guide to writing thousands of testsThe lazy programmer's guide to writing thousands of tests
The lazy programmer's guide to writing thousands of tests
Scott Wlaschin911 views
例外設計における大罪 by Takuto Wada
例外設計における大罪例外設計における大罪
例外設計における大罪
Takuto Wada68.5K views
Monadic Java by Mario Fusco
Monadic JavaMonadic Java
Monadic Java
Mario Fusco65.9K views
Pipeline oriented programming by Scott Wlaschin
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programming
Scott Wlaschin8.2K views
プログラムの処方箋~健康なコードと病んだコード by Shigenori Sagawa
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
Shigenori Sagawa8.9K views

More from Philip Schwarz

Scala Left Fold Parallelisation - Three Approaches by
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three ApproachesPhilip Schwarz
39 views44 slides
Tagless Final Encoding - Algebras and Interpreters and also Programs by
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 ProgramsPhilip Schwarz
45 views16 slides
Fusing Transformations of Strict Scala Collections with Views by
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsPhilip Schwarz
20 views28 slides
A sighting of sequence function in Practical FP in Scala by
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 ScalaPhilip Schwarz
27 views4 slides
N-Queens Combinatorial Puzzle meets Cats by
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsPhilip Schwarz
33 views386 slides
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by
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...Philip Schwarz
10 views16 slides

More from Philip Schwarz(20)

Scala Left Fold Parallelisation - Three Approaches by Philip Schwarz
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three Approaches
Philip Schwarz39 views
Tagless Final Encoding - Algebras and Interpreters and also Programs by Philip Schwarz
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
Philip Schwarz45 views
Fusing Transformations of Strict Scala Collections with Views by Philip Schwarz
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
Philip Schwarz20 views
A sighting of sequence function in Practical FP in Scala by Philip Schwarz
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
Philip Schwarz27 views
N-Queens Combinatorial Puzzle meets Cats by Philip Schwarz
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
Philip Schwarz33 views
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by Philip Schwarz
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...
Philip Schwarz10 views
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske... by Philip Schwarz
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...
Philip Schwarz249 views
Jordan Peterson - The pursuit of meaning and related ethical axioms by Philip Schwarz
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
Philip Schwarz141 views
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c... by Philip Schwarz
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Philip Schwarz100 views
Defining filter using (a) recursion (b) folding with S, B and I combinators (... by Philip Schwarz
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Philip Schwarz78 views
The Sieve of Eratosthenes - Part 1 - with minor corrections by Philip Schwarz
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarz110 views
The Sieve of Eratosthenes - Part 1 by Philip Schwarz
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarz1K views
Computer Graphics in Java and Scala - Part 1b by Philip Schwarz
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz261 views
The Expression Problem - Part 2 by Philip Schwarz
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2
Philip Schwarz650 views
Computer Graphics in Java and Scala - Part 1 by Philip Schwarz
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1
Philip Schwarz403 views
The Expression Problem - Part 1 by Philip Schwarz
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
Philip Schwarz1.2K views
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac... by Philip Schwarz
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz268 views
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ... by Philip Schwarz
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Philip Schwarz312 views
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ... by Philip Schwarz
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Philip Schwarz630 views
‘go-to’ general-purpose sequential collections - from Java To Scala by Philip Schwarz
‘go-to’ general-purpose sequential collections -from Java To Scala‘go-to’ general-purpose sequential collections -from Java To Scala
‘go-to’ general-purpose sequential collections - from Java To Scala
Philip Schwarz767 views

Recently uploaded

Neo4j y GenAI by
Neo4j y GenAI Neo4j y GenAI
Neo4j y GenAI Neo4j
35 views41 slides
How to Make the Most of Regression and Unit Testing.pdf by
How to Make the Most of Regression and Unit Testing.pdfHow to Make the Most of Regression and Unit Testing.pdf
How to Make the Most of Regression and Unit Testing.pdfAbhay Kumar
10 views9 slides
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...Deltares
7 views24 slides
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ... by
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...marksimpsongw
74 views34 slides
SUGCON ANZ Presentation V2.1 Final.pptx by
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptxJack Spektor
21 views34 slides
Software evolution understanding: Automatic extraction of software identifier... by
Software evolution understanding: Automatic extraction of software identifier...Software evolution understanding: Automatic extraction of software identifier...
Software evolution understanding: Automatic extraction of software identifier...Ra'Fat Al-Msie'deen
7 views33 slides

Recently uploaded(20)

Neo4j y GenAI by Neo4j
Neo4j y GenAI Neo4j y GenAI
Neo4j y GenAI
Neo4j35 views
How to Make the Most of Regression and Unit Testing.pdf by Abhay Kumar
How to Make the Most of Regression and Unit Testing.pdfHow to Make the Most of Regression and Unit Testing.pdf
How to Make the Most of Regression and Unit Testing.pdf
Abhay Kumar10 views
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by Deltares
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
Deltares7 views
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ... by marksimpsongw
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...
Mark Simpson - UKOUG23 - Refactoring Monolithic Oracle Database Applications ...
marksimpsongw74 views
SUGCON ANZ Presentation V2.1 Final.pptx by Jack Spektor
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptx
Jack Spektor21 views
Software evolution understanding: Automatic extraction of software identifier... by Ra'Fat Al-Msie'deen
Software evolution understanding: Automatic extraction of software identifier...Software evolution understanding: Automatic extraction of software identifier...
Software evolution understanding: Automatic extraction of software identifier...
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge... by Deltares
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...
Deltares16 views
Software testing company in India.pptx by SakshiPatel82
Software testing company in India.pptxSoftware testing company in India.pptx
Software testing company in India.pptx
SakshiPatel827 views
Neo4j : Graphes de Connaissance, IA et LLMs by Neo4j
Neo4j : Graphes de Connaissance, IA et LLMsNeo4j : Graphes de Connaissance, IA et LLMs
Neo4j : Graphes de Connaissance, IA et LLMs
Neo4j46 views
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023 by Icinga
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023Upgrading Incident Management with Icinga - Icinga Camp Milan 2023
Upgrading Incident Management with Icinga - Icinga Camp Milan 2023
Icinga36 views
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ... by Deltares
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
Deltares9 views
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema by Deltares
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - GeertsemaDSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema
Deltares12 views
Les nouveautés produit Neo4j by Neo4j
 Les nouveautés produit Neo4j Les nouveautés produit Neo4j
Les nouveautés produit Neo4j
Neo4j27 views
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove... by Deltares
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...
DSD-INT 2023 HydroMT model building and river-coast coupling in Python - Bove...
Deltares15 views
Elevate your SAP landscape's efficiency and performance with HCL Workload Aut... by HCLSoftware
Elevate your SAP landscape's efficiency and performance with HCL Workload Aut...Elevate your SAP landscape's efficiency and performance with HCL Workload Aut...
Elevate your SAP landscape's efficiency and performance with HCL Workload Aut...
HCLSoftware6 views
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ... by Donato Onofri
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR ...
Donato Onofri643 views
Roadmap y Novedades de producto by Neo4j
Roadmap y Novedades de productoRoadmap y Novedades de producto
Roadmap y Novedades de producto
Neo4j43 views

Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Haskell, Scala and Java

  • 1. Sum and Product Types The Fruit Salad & Fruit Snack Example From F# to Haskell, Scala and Java @ScottWlaschin
  • 2. type FruitSalad = { Apple: AppleVariety Banana: BananaVariety Cherries: CherryVariety } In F#, new types are built from smaller types in two ways: • By _AND_ing them together • By _OR_ing them together “AND” Types Let’s start with building types using AND. For example, we might say that to make fruit salad you need an apple and a banana and some cherries: “OR” Types The other way of building new types is by using OR. For example, we might say that for a fruit snack you need an apple or a banana or some cherries: @ScottWlaschin type FruitSnack = | Apple of AppleVariety | Banana of BananaVariety | Cherries of CherryVariety
  • 3. The varieties of fruit are themselves defined as OR types, which in this case is used similarly to an enum in other languages. type AppleVariety = | GoldenDelicious | GrannySmith | Fuji type BananaVariety = | Cavendish | GrosMichel | Manzano type CherryVariety = | Montmorency | Bing This can be read as: • An AppleVariety is either a GoldenDelicious or a GrannySmith or a Fuji, and so on. @ScottWlaschin Jargon Alert: “Product Types” and “Sum Types” The types that are built using AND are called product types. The types that are built using OR are called sum types or tagged unions or, in F# terminology, discriminated unions. In this book I will often call them choice types, because I think that best describes their role in domain modeling.
  • 4. @philip_schwarz Let’s translate that F# example into Haskell, Scala and Java.
  • 5. data FruitSalad = FruitSalad { apple :: AppleVariety, banana :: BananaVariety, cherries :: CherryVariety } data FruitSnack = Apple AppleVariety | Banana BananaVariety | Cherries CherryVariety data AppleVariety = GoldenDelicious | GrannySmith | Fuji data BananaVariety = Cavendish | GrosMichel | Manzano data CherryVariety = Montmorency | Bing case class FruitSalad( apple: AppleVariety, banana: BananaVariety, cherries: CherryVariety ) enum FruitSnack: case Apple(variety: AppleVariety) case Banana(variety: BananaVariety) case Cherries(variety: CherryVariety) enum AppleVariety: case GoldenDelicious, GrannySmith, Fuji enum BananaVariety: case Cavendish, GrosMichel, Manzano enum CherryVariety: case Montmorency, Bing record FruitSalad( AppleVariety apple, BananaVariety banana, CherryVariety cherries ) { } sealed interface FruitSnack permits Apple, Banana, Cherries { } record Apple(AppleVariety variety) implements FruitSnack { } record Banana(BananaVariety variety) implements FruitSnack { } record Cherries(CherryVariety variety) implements FruitSnack { } enum AppleVariety { GOLDEN_DELICIOUS, GRANNY_SMITH, FUJI} enum BananaVariety { CAVENDISH, GROS_MICHEL, MANZANO} enum CherryVariety { MONTMORENCY, BING}
  • 6. Now, let’s see what the behaviour is when we compare sample values and when we convert them to strings.
  • 7. data FruitSalad = FruitSalad { apple :: AppleVariety, banana :: BananaVariety, cherries :: CherryVariety } deriving (Eq, Show) data FruitSnack = Apple AppleVariety | Banana BananaVariety | Cherries CherryVariety deriving (Eq, Show) data AppleVariety = GoldenDelicious | GrannySmith | Fuji deriving (Eq, Show) data BananaVariety = Cavendish | GrosMichel | Manzano deriving (Eq, Show) data CherryVariety = Montmorency | Bing deriving (Eq, Show) main :: IO () main = let salad = FruitSalad GoldenDelicious Cavendish Montmorency sameSalad = FruitSalad GoldenDelicious Cavendish Montmorency differentSalad = FruitSalad GoldenDelicious Manzano Montmorency snack = Apple GoldenDelicious sameSnack = Apple GoldenDelicious differentSnack = Banana Cavendish in do assert (show salad == "FruitSalad {apple = GoldenDelicious, banana = Cavendish, cherries = Montmorency}") pure () assert (salad == sameSalad) return () assert (salad /= differentSalad) return () assert (show snack == "Apple GoldenDelicious") return () assert (snack == sameSnack) return () assert (snack /= differentSnack) return () -- Error: Couldn't match expected type ‘FruitSalad’ with actual type ‘FruitSnack’ assert(snack /= salad) return () -- Error: Couldn't match expected type ‘FruitSnack’ with actual type ‘AppleVariety’ assert(snack /= GoldenDelicious) return () -- Error: Couldn't match expected type ‘FruitSnack’ with actual type ‘AppleVariety’ assert(salad /= GoldenDelicious) return () To permit the ‘showing’ of FruitSalad and FruitSnack values, and also to permit the comparison of such values, we have added the following to all types: deriving (Eq, Show).
  • 8. val salad = FruitSalad(GoldenDelicious, Cavendish, Montmorency); val sameSalad = FruitSalad(GoldenDelicious, Cavendish, Montmorency); val differentSalad = FruitSalad(GoldenDelicious, Manzano, Montmorency); val snack = Apple(GoldenDelicious) val sameSnack = Apple(GoldenDelicious) val differentSnack = Banana(Cavendish) assert(salad.toString == "FruitSalad(GoldenDelicious,Cavendish,Montmorency)") assert(salad == sameSalad) assert(salad != differentSalad) assert(snack.toString == "Apple(GoldenDelicious)") assert(snack == sameSnack); assert(snack != differentSnack); // Compiler error: Values of types FruitSalad and FruitSnack cannot be compared with == or != assert(salad != snack) // Compiler error: Values of types FruitSalad and AppleVariety cannot be compared with == or != assert(salad != GoldenDelicious) // Compiler error: Values of types FruitSnack and AppleVariety cannot be compared with == or != assert(snack != GoldenDelicious) case class FruitSalad( apple: AppleVariety, banana: BananaVariety, cherries: CherryVariety ) derives CanEqual enum FruitSnack derives CanEqual: case Apple(variety: AppleVariety) case Banana(variety: BananaVariety) case Cherries(variety: CherryVariety) enum AppleVariety: case GoldenDelicious, GrannySmith, Fuji enum BananaVariety: case Cavendish, GrosMichel, Manzano enum CherryVariety: case Montmorency, Bing To prevent meaningless comparisons, e.g. comparing a salad with a snack, we have added the following to FruitSalad and FruitSnack: derives CanEqual.
  • 9. case class FruitSalad( apple: AppleVariety, banana: BananaVariety, cherries: CherryVariety ) derives CanEqual enum FruitSnack derives CanEqual: case Apple(variety: AppleVariety) case Banana(variety: BananaVariety) case Cherries(variety: CherryVariety) enum AppleVariety: case GoldenDelicious, GrannySmith, Fuji enum BananaVariety: case Cavendish, GrosMichel, Manzano enum CherryVariety: case Montmorency, Bing products (AND) degenerate products - single argument product (AND) sum (OR)
  • 10. record FruitSalad( AppleVariety apple, BananaVariety banana, CherryVariety cherries ) { } sealed interface FruitSnack permits Apple, Banana, Cherries { } record Apple(AppleVariety variety) implements FruitSnack { } record Banana(BananaVariety variety) implements FruitSnack { } record Cherries(CherryVariety variety) implements FruitSnack { } enum AppleVariety { GOLDEN_DELICIOUS, GRANNY_SMITH, FUJI} enum BananaVariety { CAVENDISH, GROS_MICHEL, MANZANO} enum CherryVariety { MONTMORENCY, BING} var salad = new FruitSalad(GOLDEN_DELICIOUS,CAVENDISH, MONTMORENCY); var sameSalad = new FruitSalad(GOLDEN_DELICIOUS,CAVENDISH, MONTMORENCY); var differentSalad = new FruitSalad(GOLDEN_DELICIOUS,MANZANO, MONTMORENCY); var snack = new Apple(GOLDEN_DELICIOUS); var sameSnack = new Apple(GOLDEN_DELICIOUS); var differentSnack = new Banana(CAVENDISH); assert(salad.toString().equals("FruitSalad[apple=GOLDEN_DELICIOUS, banana=CAVENDISH, cherries=MONTMORENCY]")); assert(salad.equals(sameSalad)); assert(!salad.equals(differentSalad)); assert(snack.toString().equals("Apple[variety=GOLDEN_DELICIOUS]")); assert(snack.equals(sameSnack)); assert(!snack.equals(differentSnack)); assert(!salad.equals(snack)); assert(!salad.equals(GOLDEN_DELICIOUS)); assert(!snack.equals(GOLDEN_DELICIOUS));
  • 11. Now, let’s see some pattern matching @philip_schwarz
  • 12. pickyCustomerReaction :: FruitSalad -> String pickyCustomerReaction (FruitSalad Fuji Cavendish Bing) = "That's my favourite combination." pickyCustomerReaction (FruitSalad GoldenDelicious _ _) = "I can't stand Golden Delicious apples." pickyCustomerReaction (FruitSalad _ Manzano Bing) = "I both love and hate this." pickyCustomerReaction (FruitSalad _ Manzano _) = "Manzano is my least favourite banana." pickyCustomerReaction (FruitSalad _ _ Bing) = "Bing are my favourite cherries." pickyCustomerReaction _ = "It will do." data FruitSalad = FruitSalad { apple :: AppleVariety, banana :: BananaVariety, cherries :: CherryVariety } deriving (Eq, Show) data FruitSnack = Apple AppleVariety | Banana BananaVariety | Cherries CherryVariety deriving (Eq, Show) data AppleVariety = GoldenDelicious | GrannySmith | Fuji deriving (Eq, Show) data BananaVariety = Cavendish | GrosMichel | Manzano deriving (Eq, Show) data CherryVariety = Montmorency | Bing deriving (Eq, Show) pickySnackerRemark :: FruitSnack -> String pickySnackerRemark (Apple Fuji) = "That's my favourite apple." pickySnackerRemark (Apple GoldenDelicious) = "I can't stand Golden Delicious apples." pickySnackerRemark (Banana Cavendish) = "That's my favourite banana." pickySnackerRemark (Banana Manzano) = "Manzano is my least favourite banana." pickySnackerRemark (Cherries Bing) = "Those are my favourite cherries." pickySnackerRemark _ = "It will do."
  • 13. case class FruitSalad( apple: AppleVariety, banana: BananaVariety, cherries: CherryVariety ) enum FruitSnack: case Apple(variety: AppleVariety) case Banana(variety: BananaVariety) case Cherries(variety: CherryVariety) enum AppleVariety: case GoldenDelicious, GrannySmith, Fuji enum BananaVariety: case Cavendish, GrosMichel, Manzano enum CherryVariety: case Montmorency, Bing val pickyCustomerReaction = salad match case FruitSalad(Fuji,Cavendish,Bing) => "That's my favourite combination." case FruitSalad(GoldenDelicious,_,_) => "I can't stand Golden Delicious apples.” case FruitSalad(_,Manzano,Bing) => "I both love and hate this." case FruitSalad(_,Manzano,_) => "Manzano is my least favourite banana." case FruitSalad(_,_,Bing) => "Bing are my favourite cherries." case _ => "It will do." val pickySnackerRemark = snack match case Apple(Fuji) => "That's my favourite apple." case Apple(GoldenDelicious) => "I can't stand Golden Delicious apples." case Banana(Cavendish) => "That's my favourite banana." case Banana(Manzano) => "Manzano is my least favourite banana." case Cherries(Bing) => "Those are my favourite cherries." case _ => "It will do."
  • 14. record FruitSalad( AppleVariety apple, BananaVariety banana, CherryVariety cherries ) { } sealed interface FruitSnack permits Apple, Banana, Cherries { } record Apple(AppleVariety variety) implements FruitSnack { } record Banana(BananaVariety variety) implements FruitSnack { } record Cherries(CherryVariety variety) implements FruitSnack { } enum AppleVariety { GOLDEN_DELICIOUS, GRANNY_SMITH, FUJI} enum BananaVariety { CAVENDISH, GROS_MICHEL, MANZANO} enum CherryVariety { MONTMORENCY, BING} String pickyCustomerReaction(FruitSalad salad) { return switch (salad) { case FruitSalad(var apple, var banana, var cherries) when apple.equals(FUJI) && banana.equals(CAVENDISH) && cherries.equals(BING) -> "That's my favourite combination."; case FruitSalad(var apple, var banana, var cherries) when apple.equals(GOLDEN_DELICIOUS) -> "I can't stand Golden Delicious apples."; case FruitSalad(var apple, var banana, var cherries) when banana.equals(MANZANO) && cherries.equals(BING) -> "I both love and hate this."; case FruitSalad(var apple, var banana, var cherries) when banana.equals(MANZANO) -> "Manzano is my least favourite banana."; case FruitSalad(var apple, var banana, var cherries) when cherries.equals(BING) -> "Bing are my favourite cherries."; default -> "It will do."; }; } String pickySnackerRemark(FruitSnack snack) { return switch (snack) { case Apple(var variety) when variety.equals(FUJI) ->"That's my favourite apple."; case Apple(var variety) when variety.equals(GOLDEN_DELICIOUS) ->"I can't stand Golden Delicious apples."; case Banana(var variety) when variety.equals(CAVENDISH) ->"That's my favourite banana."; case Banana(var variety) when variety.equals(MANZANO) ->"Manzano is my least favourite banana."; case Cherries(var variety) when variety.equals(BING) ->"Those are my favourite cherries."; default -> "It will do."; }; }
  • 15. In order to run that pattern matching code, I downloaded the Java 19 early access build.
  • 16. $ ~/Downloads/jdk-19.jdk/Contents/Home/bin/jshell --enable-preview | Welcome to JShell -- Version 19-ea | For an introduction type: /help intro jshell> record FruitSalad( ...> AppleVariety apple, ...> BananaVariety banana, ...> CherryVariety cherries ...> ) { } ...> ...> sealed interface FruitSnack permits Apple, Banana, Cherries { } ...> record Apple(AppleVariety variety) implements FruitSnack { } ...> record Banana(BananaVariety variety) implements FruitSnack { } ...> record Cherries(CherryVariety variety) implements FruitSnack { } ...> ...> enum AppleVariety {GOLDEN_DELICIOUS, GRANNY_SMITH, FUJI} ...> enum BananaVariety {CAVENDISH, GROS_MICHEL, MANZANO} ...> enum CherryVariety {MONTMORENCY, BING} | created record FruitSalad, however, it cannot be referenced until class AppleVariety, class BananaVariety, and class CherryVariety are declared | created interface FruitSnack, however, it cannot be referenced until class Apple, class Banana, and class Cherries are declared | created record Apple, however, it cannot be referenced until class AppleVariety is declared | created record Banana, however, it cannot be referenced until class BananaVariety is declared | created record Cherries, however, it cannot be referenced until class CherryVariety is declared | created enum AppleVariety | created enum BananaVariety | created enum CherryVariety jshell>
  • 17. jshell> String pickySnackerRemark(FruitSnack snack) { ...> return switch (snack) { ...> case Apple(var variety) when variety.equals(AppleVariety.FUJI) ->"That's my favourite apple."; ...> case Apple(var variety) when variety.equals(AppleVariety.GOLDEN_DELICIOUS) ->"I can't stand Golden Delicious apples."; ...> case Banana(var variety) when variety.equals(BananaVariety.CAVENDISH) ->"That's my favourite banana."; ...> case Banana(var variety) when variety.equals(BananaVariety.MANZANO) ->"Manzano is my least favourite banana."; ...> case Cherries(var variety) when variety.equals(CherryVariety.BING) ->"Those are my favourite cherries."; ...> default -> "It will do."; ...> }; ...> } | created method pickySnackerRemark(FruitSnack) jshell> FruitSnack snack = new Banana(BananaVariety.MANZANO); snack ==> Banana[variety=MANZANO] jshell> pickySnackerRemark(snack) $11 ==> "Manzano is my least favourite banana.” jshell>
  • 18. jshell> String pickyCustomerReaction(FruitSalad salad) { ...> return switch (salad) { ...> case FruitSalad(var apple, var banana , var cherries) ...> when apple.equals(AppleVariety.FUJI) && banana.equals(BananaVariety.CAVENDISH) && cherries.equals(CherryVariety.BING) -> ...> "That's my favourite combination."; ...> case FruitSalad(var apple, var banana , var cherries) ...> when apple.equals(AppleVariety.GOLDEN_DELICIOUS) -> ...> "I can't stand Golden Delicious apples."; ...> case FruitSalad(var apple, var banana , var cherries) ...> when banana.equals(BananaVariety.MANZANO) -> ...> "Manzano is my least favourite banana."; ...> case FruitSalad(var apple, var banana , var cherries) ...> when cherries.equals(CherryVariety.BING) -> ...> "Bing are my favourite cherries."; ...> case FruitSalad(var apple, var banana , var cherries) ...> when banana.equals(BananaVariety.MANZANO) && cherries.equals(CherryVariety.BING) -> ...> "I both love and hate this."; ...> default -> "It will do."; ...> }; ...> } | created method pickyCustomerReaction(FruitSalad) jshell> var salad = new FruitSalad(AppleVariety.GOLDEN_DELICIOUS,BananaVariety.CAVENDISH, CherryVariety.MONTMORENCY); salad ==> FruitSalad[apple=GOLDEN_DELICIOUS, banana=CAVENDISH, cherries=MONTMORENCY] jshell> pickyCustomerReaction(salad); $14 ==> "I can't stand Golden Delicious apples." jshell>
  • 19. I had a go at applying those suggestions. The first one was fine. When I tried the second one, I ran into some issues. If I find out more from Brian Goetz, or I manage to resolve the issue, then I’ll publish a new version of this deck.
  • 20. That’s all. I hope you found it useful. @philip_schwarz