Slide 1

Slide 1 text

Template-Oriented-Programming to Ship Faster / ! @GopalAkshintala " overfullstack.github.io 1

Slide 2

Slide 2 text

I'm Gopal S Akshintala ! Software Engineer @ Salesforce ! @GopalAkshintala ! overfullstack.github.io/about-me/ / ! @GopalAkshintala " overfullstack.github.io 2

Slide 3

Slide 3 text

Disclaimer* • Basics First • More Why & How, less What • Don't stress yourself on low-level details. Just sit- back and relax ! • You may keep your mics on " (Silence is intimidating than FPH) / ! @GopalAkshintala " overfullstack.github.io 3

Slide 4

Slide 4 text

Why FP? • Surely, not because it feels cool • Changes the way you think about coding. • Code like Maths equations and deravations. • These are Design patterns too. • Concise • High Quality (Bug free) • Expressive • Shared Vocab / ! @GopalAkshintala " overfullstack.github.io 4

Slide 5

Slide 5 text

Ok! What are we going to do today? ! / ! @GopalAkshintala " overfullstack.github.io 5

Slide 6

Slide 6 text

What is Monomorphic? • Number sorting algorithms • How can we reuse it for sorting strings? / ! @GopalAkshintala " overfullstack.github.io 6

Slide 7

Slide 7 text

What is Polymorphic? • Types of Polymorphism • Subtype with Inheritance • Parametric with Generics E.g., List • Ad-hoc Polymorphism with Typeclasses / ! @GopalAkshintala " overfullstack.github.io 7

Slide 8

Slide 8 text

Typeclasses • What is the heart of all our sorting algorithms? • Comparator 8 It's a typeclass • What is the connection between a DataType and a Typeclass? / ! @GopalAkshintala " overfullstack.github.io 8

Slide 9

Slide 9 text

Comparator val appleComparator = Comparator { apple1, apple2 4 apple1?.let { first 4 apple2?.let { second 4 first.weight.compareTo(second.weight) } ?: 1 } ?: 0 } listOf(Apple(3), Apple(1), Apple(2)).sortedWith(appleComparator) I J [Apple(weight=1), Apple(weight=2), Apple(weight=3)] / ! @GopalAkshintala " overfullstack.github.io 9

Slide 10

Slide 10 text

Let's create a Simple Typeclass interface AdderTC { fun add(a: AddableT, b: AddableT): AddableT 8 AddableT type should be a member of AdderTC type class by implementing all its methods. fun addAll(addables: List) = addables.reduce { acc, i C add(acc, i) } } / ! @GopalAkshintala " overfullstack.github.io 10

Slide 11

Slide 11 text

AdderTC To make Int a member of AdderTC: val intCombine = object : AdderTC { override fun add(a: Int, b: Int) = a + b } / ! @GopalAkshintala " overfullstack.github.io 11

Slide 12

Slide 12 text

AdderTC To make String a member of AdderTC: val stringCombine = object : AdderTC { override fun add(a: String, b: String) = a + b } / ! @GopalAkshintala " overfullstack.github.io 12

Slide 13

Slide 13 text

AdderTC println(intCombine.addAll(listOf(1, 2, 3))) : ; 6 println(stringCombine.addAll(listOf("a", "b", "c"))) : ; abc This is Ad-hoc Polymorphism / ! @GopalAkshintala " overfullstack.github.io 13

Slide 14

Slide 14 text

Before moving-on, any Questions / ! @GopalAkshintala " overfullstack.github.io 14

Slide 15

Slide 15 text

Another Monomorphic Example Email Validation fun validateEmail(email: String): Either = if (email.contains("@", false)) { if (email.length B 250) { Unit.right() } else { ValidationError.EmailMaxLength(250).left() } } else { ValidationError.DoesNotContain("@").left() } This is stuck with Fail fast / ! @GopalAkshintala " overfullstack.github.io 15

Slide 16

Slide 16 text

Another Monomorphic Example private fun validateEmailErrorAccumulation(email: String): Either, Unit> { val errorList = mutableListOf() if (!email.contains("@", false)) { errorList.add(ValidationError.DoesNotContain("@")) } if (email.length > 250) { errorList.add(ValidationError.EmailMaxLength(250)) } return if (errorList.isNotEmpty()) errorList.left() else Unit.right() } This is stuck with Error Accumulation / ! @GopalAkshintala " overfullstack.github.io 16

Slide 17

Slide 17 text

How to make it Polymorphic? • What makes-up the heart of this algorithm? • The Effect of being Valid/Invalid • The Effect of combining all validation errors • How to abstract them out? / ! @GopalAkshintala " overfullstack.github.io 17

Slide 18

Slide 18 text

The Polymorphic magic The Orchestration fun ValidatorAE.validateEmailWithRules(email: String): Kind

Slide 19

Slide 19 text

The Polymorphic magic The Fail-Fast private fun validateEmailFailFastX(email: String): Either, Tuple2().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 19

Slide 20

Slide 20 text

The Polymorphic magic The Error Accumulation private fun validateEmailErrorAccumulationX(email: String): Validated, Tuple2().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 20

Slide 21

Slide 21 text

Λrrow made the magic possible arrow-kt.io / ! @GopalAkshintala " overfullstack.github.io 21

Slide 22

Slide 22 text

Some Intimidating Terminology The Typeclass Arsenal • Functor • Monad • Applicative • ApplicativeError • 3(many more) DataTypes such as Option, Either, Validated implement these interfaces. / ! @GopalAkshintala " overfullstack.github.io 22

Slide 23

Slide 23 text

Typeclasses : DataTypes Type class Combinators List Option Either EitherApplicati ve EitherApplicati veError Validated ValidatedApplic ative ValidatedApplic ativeError Functor map, lift ✓ ✓ ✓ ✓ Monad flatMap, flatten ✓ ✓ ✓ ✓ Applicative pure, ap ✓ ✓ ✓ ✓ ApplicativeErro r raiseError, catch ✕ ✓ ✓ ✓ / ! @GopalAkshintala " overfullstack.github.io 23

Slide 24

Slide 24 text

Functor Is any Datatype that implements a map operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 24

Slide 25

Slide 25 text

Functor Is any Datatype that implements a map operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 25

Slide 26

Slide 26 text

Functor Is any Datatype that implements a map operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 26

Slide 27

Slide 27 text

Functor listOf("a", "b", "c").map { it.toUpperCase() } Option("info").map { it.toUpperCase() } / ! @GopalAkshintala " overfullstack.github.io 27

Slide 28

Slide 28 text

Monad Is any Datatype that implements a flatMap operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 28

Slide 29

Slide 29 text

Monad Is any Datatype that implements a flatMap operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 29

Slide 30

Slide 30 text

Monad Is any Datatype that implements a flatMap operation source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 30

Slide 31

Slide 31 text

Monad Monad is used for Dependent operations Option("userName").flatMap { getFirstName(it) } fun getFirstName(userName: String) : Option = Option("firstName") / ! @GopalAkshintala " overfullstack.github.io 31

Slide 32

Slide 32 text

Applicative Is anything that has an apply source source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 32

Slide 33

Slide 33 text

Applicative • But why do you want to put a function inside a box? • You can do some interesting things, to combine two wrapped values, using an operation called product. / ! @GopalAkshintala " overfullstack.github.io 33

Slide 34

Slide 34 text

Applicative Applicative is used for Independent operations fun getFirstName(userName: String) : Option = Option("firstName") fun getLastName(userName: String) : Option = Option("lastName") fun getFullName(userName: String) = getFirstName(userName).product(getLastName(userName)) .map { names: Tuple2 C "${names.a} ${names.b}" } G H Some(firstName lastName) product internally calls apply, by converting its wrapped argument to a wrapped function, but let's not get into details now. / ! @GopalAkshintala " overfullstack.github.io 34

Slide 35

Slide 35 text

Applicative It's all Maths! println(None.product(Some(3))) 3 5 None println(Some(3).product(None)) 3 5 None / ! @GopalAkshintala " overfullstack.github.io 35

Slide 36

Slide 36 text

Meditative

Well, it's not a typeclass, let's pause, take a deep breathe and revise. Coz, we are gonna shift gears! / ! @GopalAkshintala " overfullstack.github.io 36

Slide 37

Slide 37 text

ApplicativeError It is same as Applicative, with an E! Ya it has a raiseError(e: E) method. Either.applicativeError().raiseError("Dark") : < Left(Dark) Option.applicativeError().raiseError(Unit) : < None • applicativeError() is just a companion method on the DataType to get ApplicativeInstance • When raiseError(e) is called, the Datatype transitions into the dark side. / ! @GopalAkshintala " overfullstack.github.io 37

Slide 38

Slide 38 text

ApplicativeError Why is this important? Any guesses, how this raiseError() can help our validation? • The Effect of being Valid/Invalid - This can be taken care by the Left and Right states and raiseError takes care of the transition. • The Effect of combining all validation errors - This can be taken care by the product. • But what about Strategy switching between Fail-Fast and Error- Accumulation? / ! @GopalAkshintala " overfullstack.github.io 38

Slide 39

Slide 39 text

ApplicativeError We have ready made type classes for these strategies: EitherApplicativeError : ApplicativeError, L> ValidatedApplicativeError : ApplicativeError, E> • Again, let's not get into the details, but let's understand the more important HOW? • Hint: product implementation in both these typeclasses is going to make the difference / ! @GopalAkshintala " overfullstack.github.io 39

Slide 40

Slide 40 text

EitherApplicativeError Product Matrix Invalid Valid Invalid Invalid Invalid Valid Invalid Product / ! @GopalAkshintala " overfullstack.github.io 40

Slide 41

Slide 41 text

ValidatedApplicativeError Product Matrix Invalid Valid Invalid Accumulate Errors Add First Error Valid Add First Error Product / ! @GopalAkshintala " overfullstack.github.io 41

Slide 42

Slide 42 text

Controlling Strategy with Effect The Strategies fun failFast(): FailFast = Either.applicativeError() fun errorAccumulation(): ErrorAccumulation = Validated.applicativeError(NonEmptyList.semigroup()) / ! @GopalAkshintala " overfullstack.github.io 42

Slide 43

Slide 43 text

Controlling Strategy with Effect The Entry point of execution private fun validateEmailFailFastX(email: String): Either, Tuple2().run { validateEmailWithRules(email).fix() } private fun validateEmailErrorAccumulationX(email: String): Validated, Tuple2().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 43

Slide 44

Slide 44 text

Controlling Strategy with Effect The Product fun ValidatorAE.validateEmailWithRules(email: String): Kind

Slide 45

Slide 45 text

Controlling Strategy with Effect The Validations private fun ValidatorAE.contains(email: String, needle: String): Kind = if (email.contains(needle, false)) just(email) else raiseError(ValidationError.DoesNotContain(needle).nel()) private fun ValidatorAE.maxLength(email: String, maxLength: Int): Kind = if (email.length H maxLength) just(email) else raiseError(ValidationError.EmailMaxLength(maxLength).nel()) / ! @GopalAkshintala " overfullstack.github.io 45

Slide 46

Slide 46 text

Done! ! / ! @GopalAkshintala " overfullstack.github.io 46

Slide 47

Slide 47 text

Please Feedback DM on twitter @GopalAkshintala Email me - gopal.akshintala@gmail.com / ! @GopalAkshintala " overfullstack.github.io 47

Slide 48

Slide 48 text

Show me the Code github.com/overfullstack/ad-hoc-poly / ! @GopalAkshintala " overfullstack.github.io 48

Slide 49

Slide 49 text

Let's Slack together! bit.ly/overfullstack-slack / ! @GopalAkshintala " overfullstack.github.io 49

Slide 50

Slide 50 text

Thanks! ! / ! @GopalAkshintala " overfullstack.github.io 50