SlideShare a Scribd company logo
1 of 20
Download to read offline
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

More Related Content

What's hot

Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
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
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーyoku0825
 
goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?mori takuma
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたってTsuyoshi Matsudate
 
頑張りすぎないScala
頑張りすぎないScala頑張りすぎないScala
頑張りすぎないScalatakezoe
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~Nobuhisa Koizumi
 
Scala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたScala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたKazuyuki TAKASE
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードShigenori Sagawa
 
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"Kentaro Yoshida
 
The Expression Problem - Part 1
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1Philip 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
‘go-to’ general-purpose sequential collections - from Java To ScalaPhilip Schwarz
 
Unified JVM Logging
Unified JVM LoggingUnified JVM Logging
Unified JVM LoggingYuji Kubota
 
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...Philip Schwarz
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話Yoshitaka Kawashima
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
 
Scala警察のすすめ
Scala警察のすすめScala警察のすすめ
Scala警察のすすめtakezoe
 

What's hot (20)

Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
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 1
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたって
 
Kleisli Composition
Kleisli CompositionKleisli Composition
Kleisli Composition
 
頑張りすぎないScala
頑張りすぎないScala頑張りすぎないScala
頑張りすぎないScala
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~
 
Scala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたScala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみた
 
Applicative Functor
Applicative FunctorApplicative Functor
Applicative Functor
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
 
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
 
The Expression Problem - Part 1
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
 
‘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
‘go-to’ general-purpose sequential collections - from Java To Scala
 
Unified JVM Logging
Unified JVM LoggingUnified JVM Logging
Unified JVM Logging
 
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
 
Are Design Patterns Dead?
Are Design Patterns Dead?Are Design Patterns Dead?
Are Design Patterns Dead?
 
Scala警察のすすめ
Scala警察のすすめScala警察のすすめ
Scala警察のすすめ
 

More from Philip Schwarz

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 seriesPhilip Schwarz
 
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 seriesPhilip Schwarz
 
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 seriesPhilip Schwarz
 
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 seriesPhilip Schwarz
 
Scala Left Fold Parallelisation - Three Approaches
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three ApproachesPhilip Schwarz
 
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 ProgramsPhilip Schwarz
 
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 ViewsPhilip Schwarz
 
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 ScalaPhilip Schwarz
 
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 ScalaPhilip Schwarz
 
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 ScalaPhilip Schwarz
 
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsPhilip 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...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
 
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...Philip Schwarz
 
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 examplePhilip Schwarz
 
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 examplePhilip 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...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...Philip Schwarz
 
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 axiomsPhilip 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...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...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 (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Philip Schwarz
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
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 correctionsPhilip Schwarz
 

More from Philip Schwarz (20)

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...
 
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
 
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...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
 
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 (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
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
 

Recently uploaded

Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 

Recently uploaded (20)

Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 

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