Slide 1

Slide 1 text

SEALING IN THE FLAVOR

Slide 2

Slide 2 text

SIMON VERGAUWEN

Slide 3

Slide 3 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ ….

Slide 4

Slide 4 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY UNFAMILIAR ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ ….

Slide 5

Slide 5 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY UNFAMILIAR ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ …. ▸ Chainable

Slide 6

Slide 6 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY UNFAMILIAR ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ …. ▸ Chainable ▸ Aggregatable

Slide 7

Slide 7 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY UNFAMILIAR ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ …. ▸ Chainable ▸ Aggregatable ▸ Mappable

Slide 8

Slide 8 text

SEALING IN THE FLAVOUR FUNCTIONAL PROGRAMMING IS SCARY UNFAMILIAR ▸ Monoid ▸ Monad ▸ Functor ▸ Catamorphism ▸ …. ▸ Chainable ▸ Aggregatable ▸ Mappable ▸ Collapsable

Slide 9

Slide 9 text

SEALING IN THE FLAVOUR WHAT ARE SEALED CLASSES? ▸ Tagged Union, Sum type “A data structure used to hold a value that could take several different, but fixed, types. Only one of the types can be in use at any one time.

Slide 10

Slide 10 text

SEALING IN THE FLAVOUR MODELLING DATA data class Visitor( val id: String?, val name: String?, val lastName: String?, val email: String?, val isEmailVerified: Boolean, val isAnonymous: Boolean )

Slide 11

Slide 11 text

SEALING IN THE FLAVOUR MODELLING DATA data class Visitor( val id: String?, val name: String?, val lastName: String?, val email: String?, val isEmailVerified: Boolean, val isAnonymous: Boolean ) DOMAIN LOGIC!?

Slide 12

Slide 12 text

SEALING IN THE FLAVOUR MODELLING DATA data class Visitor( val id: String?, val name: String?, val lastName: String?, val email: String?, val isEmailVerified: Boolean, val isAnonymous: Boolean ) DOMAIN LOGIC!? WHEN CAN WHAT FIELD BE NULL? BOOLEAN FLAGS?

Slide 13

Slide 13 text

SEALING IN THE FLAVOUR MODELLING DATA abstract class Visitor { data class VerifiedUser( val id: String, val name: String, val lastName: String, val email: String ): Visitor() }

Slide 14

Slide 14 text

SEALING IN THE FLAVOUR MODELLING DATA abstract class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser( val name: String, val lastName: String, val email: String ): Visitor() }

Slide 15

Slide 15 text

SEALING IN THE FLAVOUR MODELLING DATA abstract class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() }

Slide 16

Slide 16 text

SEALING IN THE FLAVOUR MODELLING DATA abstract class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } CLEAR MODEL OF OUR DOMAIN AND FIELDS

Slide 17

Slide 17 text

SEALING IN THE FLAVOUR MODELLING DATA //com.github.nomisRev.Visitor.kt abstract class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } //some.hackery.HackyUser.kt data class HackyUser(val hackedId: String): Visitor() MIGHT BREAK MY LOGIC

Slide 18

Slide 18 text

SEALING IN THE FLAVOUR MODELLING DATA //com.github.nomisRev.Visitor.kt sealed class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } //some.hackery.HackyUser.kt data class HackyUser(val hackedId: String): Visitor() SEALED (CLOSED) INHERITANCE

Slide 19

Slide 19 text

SEALING IN THE FLAVOUR MODELLING DATA //com.github.nomisRev.Visitor.kt sealed class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } //some.hackery.HackyUser.kt data class HackyUser(val hackedId: String): Visitor() SEALED (CLOSED) INHERITANCE

Slide 20

Slide 20 text

SEALING IN THE FLAVOUR MODELLING DATA sealed class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } fun sayHello(visitor: Visitor): String = when (visitor) { is Visitor.VerifiedUser -> "Hello ${visitor.name}," + "here are you settings/${visitor.id}" is Visitor.UnverifiedUser -> "Hello ${visitor.name}," + "we have sent a verification email to ${visitor.email}" is Visitor.Anonymous -> "Hello, would you like to register?" }

Slide 21

Slide 21 text

SEALING IN THE FLAVOUR MODELLING DATA sealed class Visitor { data class VerifiedUser(...): Visitor() data class UnverifiedUser(...): Visitor() object Anonymous : Visitor() } fun sayHello(visitor: Visitor): String = when (visitor) { is Visitor.VerifiedUser -> "Hello ${visitor.name}," + "here are you settings/${visitor.id}" is Visitor.UnverifiedUser -> "Hello ${visitor.name}," + "we have sent a verification email to ${visitor.email}" is Visitor.Anonymous -> "Hello, would you like to register?" } SMART CASTING

Slide 22

Slide 22 text

SEALING IN THE FLAVOUR SEALED CLASS VS ABSTRACT CLASS ▸ Compiler knows all subtypes ▸ No inheritance hierarchy - type definition ▸ Strong guarantees about types

Slide 23

Slide 23 text

SEALING IN THE FLAVOUR EXAMPLE sealed class MathExpression { data class Add( val left: MathExpression, val right: MathExpression ) : MathExpression() data class Subtract( val left: MathExpression, val right: MathExpression ) : MathExpression() data class Number(val value: Int) : MathExpression() }

Slide 24

Slide 24 text

SEALING IN THE FLAVOUR EXAMPLE sealed class MathExpression { data class Add(left, right) : MathExpression() data class Subtract(left, right) : MathExpression() data class Number(val value: Int) : MathExpression() fun eval(): Int = when (this) { } } Error:(9,23) Kotlin:’when’ expression must be exhaustive,
 add `is Add`, `is Subtract` and `is Number` branches
 or `else` branch instead.

Slide 25

Slide 25 text

SEALING IN THE FLAVOUR EXAMPLE sealed class MathExpression { data class Add(left, right) : MathExpression() data class Subtract(left, right) : MathExpression() data class Number(val value: Int) : MathExpression() fun eval(): Int = when (this) { is MathExpression.Add -> left.eval() + right.eval() is MathExpression.Subtract -> left.eval() - right.eval() is MathExpression.Number -> value } }

Slide 26

Slide 26 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP

Slide 27

Slide 27 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class OOPMathExpression { abstract fun eval(): Int data class Add(left, right) : OOPMathExpression() { override fun eval() = left.eval() + right.eval() } data class Subtract(left, right) : OOPMathExpression() { override fun eval() = left.eval() - right.eval() } data class Number(value) : OOPMathExpression() { override fun eval() = value } }

Slide 28

Slide 28 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class OOPMathExpression { abstract fun eval(): Int data class Add(left, right) : OOPMathExpression() { override fun eval() = left.eval() + right.eval() } data class Subtract(left, right) : OOPMathExpression() { override fun eval() = left.eval() - right.eval() } data class Number(value) : OOPMathExpression() { override fun eval() = value } data class Division(left, right) : OOPMathExpression() } COMPILER DEMANDS IMPLEMENTATION

Slide 29

Slide 29 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class OOPMathExpression { abstract fun eval(): Int data class Add(left, right) : OOPMathExpression() { override fun eval() = left.eval() + right.eval() } data class Subtract(left, right) : OOPMathExpression() { override fun eval() = left.eval() - right.eval() } data class Number(value) : OOPMathExpression() { override fun eval() = value } data class Division(left, right) : OOPMathExpression() { override fun eval() = left.eval() / right.eval() } }

Slide 30

Slide 30 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class MathExpression { data class Add(left, right) : MathExpression() data class Subtract(left, right) : MathExpression() data class Number(value) : MathExpression() data class Division(left, right): MathExpression() fun eval(): Int = when (this) { is MathExpression.Add -> left.eval() + right.eval() is MathExpression.Subtract -> left.eval() - right.eval() is MathExpression.Number -> value is MathExpression.Add -> left.eval() / right.eval() } }

Slide 31

Slide 31 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class MathExpression { data class Add(left, right) : MathExpression() data class Subtract(left, right) : MathExpression() data class Number(value) : MathExpression() data class Division(left, right): MathExpression() fun eval(): Int = when (this) { is MathExpression.Add -> left.eval() + right.eval() is MathExpression.Subtract -> left.eval() - right.eval() is MathExpression.Number -> value is MathExpression.Add -> left.eval() / right.eval() } } ANY ISSUES HERE???

Slide 32

Slide 32 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP sealed class MathExpression { data class Add(left, right) : MathExpression() data class Subtract(left, right) : MathExpression() data class Number(value) : MathExpression() data class Division(left, right): MathExpression() fun eval(): Int = when (this) { is MathExpression.Add -> left.eval() + right.eval() is MathExpression.Subtract -> left.eval() - right.eval() is MathExpression.Number -> value is MathExpression.Add -> left.eval() / right.eval() } } ANY ISSUES HERE??? JAVA.LANG.ARITHMETICEXCEPTION: / BY ZERO AT MATHEXPRESSION2.EVAL(MATHEXPRESSION2.KT:13)

Slide 33

Slide 33 text

SEALING IN THE FLAVOUR LET’S ADD OPERATION. OOP VS FP Add new method Add new data Polymorpish Change existing code Existing code unchanged Pattern matching Existing code unchanged Change existing code

Slide 34

Slide 34 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS sealed class MathResult { data class Success(val value: Int) : MathResult() data class Failure(val exception: Throwable) : MathResult() }

Slide 35

Slide 35 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS sealed class MathExpression { data class Add(left, right) : MathExpression() data class Number(value: Int) : MathExpression() data class Division(left, right) : MathExpression() fun eval(): MathResult = when (this) { ... } }

Slide 36

Slide 36 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Number -> MathResult.Success(value) }

Slide 37

Slide 37 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Add -> { val lR: MathResult = left.eval() } is MathExpression.Number -> Success(value) }

Slide 38

Slide 38 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Add -> { val lR: MathResult = left.eval() when (lR) { is Failure -> lR is Success -> { } } } is MathExpression.Number -> Success(value) }

Slide 39

Slide 39 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() } } } is MathExpression.Number -> Success(value) }

Slide 40

Slide 40 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lR.value + rR.value) } } } } is MathExpression.Number -> Success(value) }

Slide 41

Slide 41 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> { val lR: MathResult = left.eval() } is MathExpression.Add -> {…} is MathExpression.Number -> Success(value) }

Slide 42

Slide 42 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> { val lR = left.eval() when (lR) { is Failure -> lR is Success -> { } } is MathExpression.Add -> {…} is MathExpression.Number -> Success(value) }

Slide 43

Slide 43 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> { val lR = left.eval() when (lR) { is Failure -> lR is Success -> { val rR = right.eval() } } is MathExpression.Add -> {…} is MathExpression.Number -> Success(value) }

Slide 44

Slide 44 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> { val lR = left.eval() when (lR) { is Failure -> lR is Success -> { val rR = right.eval() when (rR) { is Failure -> rR is Success -> if (rR.value != 0) Success(lR.value / rR.value) else Failure(ArithmeticException(“Cannot / by zero")) } } } } is MathExpression.Add -> {…} is MathExpression.Number -> Success(value) }

Slide 45

Slide 45 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> if (rR.value != 0) Success(lR.value / rR.value) else Failure(ArithmeticException(“Cannot / by zero")) is MathExpression.Add -> Success(lR.value + rR.value) is MathExpression.Number -> Success(value) }

Slide 46

Slide 46 text

SEALING IN THE FLAVOUR HANDLE EXCEPTIONS fun eval(): MathResult = when (this) { is MathExpression.Division -> if (rR.value != 0) lR.value / rR.value else ArithmeticException(“Cannot / by zero”) is MathExpression.Add -> lR.value + rR.value is MathExpression.Number -> value }

Slide 47

Slide 47 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lR.value + rR.value) } } } }

Slide 48

Slide 48 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lR.value + rR.value) } } } } IF FAILURE IMMEDIATELY RETURN ELSE COMPUTE NEW MATH RESULT

Slide 49

Slide 49 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lR.value + rR.value) } } } } IF FAILURE IMMEDIATELY RETURN ELSE COMPUTE NEW MATH RESULT fun flatMap(f: (Int) -> MathResult): MathResult = when (this) { is MathResult.Failure -> this is MathResult.Success -> f(value) }

Slide 50

Slide 50 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { left.eval().flatMap { lValue -> val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lValue + rR.value) } } }

Slide 51

Slide 51 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { left.eval().flatMap { lValue -> val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lValue + rR.value) } } } IF FAILURE IMMEDIATELY RETURN ELSE COMPUTE NEW VALUE

Slide 52

Slide 52 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { left.eval().flatMap { lValue -> val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lValue + rR.value) } } } IF FAILURE IMMEDIATELY RETURN ELSE COMPUTE NEW VALUE fun map(f: (Int) -> Int): MathResult = when(this) { is MathResult.Failure -> this is MathResult.Success -> MathResult.Success(f(value)) }

Slide 53

Slide 53 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { left.eval().flatMap { lValue -> right.eval().map { rValue -> lValue + rValue } } }

Slide 54

Slide 54 text

SEALING IN THE FLAVOUR GO AWAY BOILERPLATE! is MathExpression.Add -> { val lResult: MathResult = left.eval() when (lResult) { is Failure -> lResult is Success -> { val rR: MathResult = right.eval() when (rR) { is Failure -> rResult is Success -> Success(lR.value + rR.value) } } } } is MathExpression.Add -> { left.eval().flatMap { lValue -> right.eval().map { rValue -> lValue + rValue } } }

Slide 55

Slide 55 text

SEALING IN THE FLAVOUR HANDLING ERRORS - NULL IS BAD! ▸ Bypasses type system ▸ Abused to model non existing values. ▸ Nulls can appear anywhere, in any value or variable. ▸ No way to control them as they can be anything. ▸ Hard to debug and leads to bugs

Slide 56

Slide 56 text

SEALING IN THE FLAVOUR HANDLING ERRORS - EXCEPTIONS ARE BAD! ▸ Bypasses type system ▸ Can appear anywhere (unchecked). ▸ Abused to model non existing values. ▸ Can be hard to debug and leads to bugs

Slide 57

Slide 57 text

SEALING IN THE FLAVOUR SOLUTIONS

Slide 58

Slide 58 text

SEALING IN THE FLAVOUR SOLUTIONS sealed class Option { data class Some(val value: T) : Option() object None : Option() }

Slide 59

Slide 59 text

SEALING IN THE FLAVOUR SOLUTIONS sealed class Try { data class Success(val value: T) : Try() data class Failure(val exception: Throwable) : Try() } sealed class Option { data class Some(val value: T) : Option() object None : Option() }

Slide 60

Slide 60 text

SEALING IN THE FLAVOUR SOLUTIONS sealed class Try { data class Success(val value: T) : Try() data class Failure(val exception: Throwable) : Try() } sealed class Option { data class Some(val value: T) : Option() object None : Option() } sealed class Either { data class Left(val left: T) : Either() data class Right(val right: R) : Either() }

Slide 61

Slide 61 text

https://speakerdeck.com/nomisrev
 https://github.com/nomisRev Any questions?