Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Template-Oriented-Programming (TOP) to Ship Faster

Template-Oriented-Programming (TOP) to Ship Faster

Gopal S Akshintala

April 04, 2020
Tweet

More Decks by Gopal S Akshintala

Other Decks in Programming

Transcript

  1. I'm Gopal S Akshintala ! Software Engineer @ Salesforce !

    @GopalAkshintala ! overfullstack.github.io/about-me/ / ! @GopalAkshintala " overfullstack.github.io 2
  2. 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
  3. 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
  4. Ok! What are we going to do today? ! /

    ! @GopalAkshintala " overfullstack.github.io 5
  5. What is Monomorphic? • Number sorting algorithms • How can

    we reuse it for sorting strings? / ! @GopalAkshintala " overfullstack.github.io 6
  6. What is Polymorphic? • Types of Polymorphism • Subtype with

    Inheritance • Parametric with Generics E.g., List<T> • Ad-hoc Polymorphism with Typeclasses / ! @GopalAkshintala " overfullstack.github.io 7
  7. Typeclasses • What is the heart of all our sorting

    algorithms? • Comparator<T> 8 It's a typeclass • What is the connection between a DataType and a Typeclass? / ! @GopalAkshintala " overfullstack.github.io 8
  8. Comparator<T> val appleComparator = Comparator<Apple> { 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
  9. Let's create a Simple Typeclass interface AdderTC<AddableT> { 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<AddableT>) = addables.reduce { acc, i C add(acc, i) } } / ! @GopalAkshintala " overfullstack.github.io 10
  10. AdderTC<Int> To make Int a member of AdderTC<AddableT>: val intCombine

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

    = object : AdderTC<String> { override fun add(a: String, b: String) = a + b } / ! @GopalAkshintala " overfullstack.github.io 12
  12. AdderTC<AddableT> 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
  13. Another Monomorphic Example Email Validation fun validateEmail(email: String): Either<ValidationError, Unit>

    = 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
  14. Another Monomorphic Example private fun validateEmailErrorAccumulation(email: String): Either<MutableList<ValidationError>, Unit> {

    val errorList = mutableListOf<ValidationError>() 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
  15. 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
  16. The Polymorphic magic The Orchestration fun <S> ValidatorAE<S, ValidationError>.validateEmailWithRules(email: String):

    Kind<S, Tuple2<String, StringC = tupledN( contains(email, "@"), maxLength(email, 250) ).handleErrorWith { raiseError(it) } / ! @GopalAkshintala " overfullstack.github.io 18
  17. The Polymorphic magic The Fail-Fast private fun validateEmailFailFastX(email: String): Either<NonEmptyList<ValidationError>,

    Tuple2<String, StringC = failFast<ValidationError>().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 19
  18. The Polymorphic magic The Error Accumulation private fun validateEmailErrorAccumulationX(email: String):

    Validated<NonEmptyList<ValidationError>, Tuple2<String, StringC = errorAccumulation<ValidationError>().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 20
  19. 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
  20. 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
  21. Functor<F> Is any Datatype that implements a map operation source

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

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

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

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

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

    source Functors, Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 30
  27. Monad<F> Monad is used for Dependent operations Option("userName").flatMap { getFirstName(it)

    } fun getFirstName(userName: String) : Option<String> = Option("firstName") / ! @GopalAkshintala " overfullstack.github.io 31
  28. Applicative<F> Is anything that has an apply source source Functors,

    Applicatives, And Monads In Pictures | adit.io / ! @GopalAkshintala " overfullstack.github.io 32
  29. Applicative<F> • 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
  30. Applicative<F> Applicative is used for Independent operations fun getFirstName(userName: String)

    : Option<String> = Option("firstName") fun getLastName(userName: String) : Option<String> = Option("lastName") fun getFullName(userName: String) = getFirstName(userName).product(getLastName(userName)) .map { names: Tuple2<String, String> 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
  31. Meditative<P> 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
  32. ApplicativeError<F, E> It is same as Applicative<F>, with an E!

    Ya it has a raiseError(e: E) method. Either.applicativeError<String>().raiseError<Unit>("Dark") : < Left(Dark) Option.applicativeError().raiseError<Unit>(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
  33. ApplicativeError<F, E> 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
  34. ApplicativeError<F, E> We have ready made type classes for these

    strategies: EitherApplicativeError<L> : ApplicativeError<EitherPartialOf<L>, L> ValidatedApplicativeError<E> : ApplicativeError<ValidatedPartialOf<E>, 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
  35. EitherApplicativeError Product Matrix Invalid Valid Invalid Invalid Invalid Valid Invalid

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

    Error Valid Add First Error Product / ! @GopalAkshintala " overfullstack.github.io 41
  37. Controlling Strategy with Effect The Strategies fun <E> failFast(): FailFast<E>

    = Either.applicativeError() fun <E> errorAccumulation(): ErrorAccumulation<E> = Validated.applicativeError(NonEmptyList.semigroup()) / ! @GopalAkshintala " overfullstack.github.io 42
  38. Controlling Strategy with Effect The Entry point of execution private

    fun validateEmailFailFastX(email: String): Either<NonEmptyList<ValidationError>, Tuple2<String, StringC = failFast<ValidationError>().run { validateEmailWithRules(email).fix() } private fun validateEmailErrorAccumulationX(email: String): Validated<NonEmptyList<ValidationError>, Tuple2<String, StringC = errorAccumulation<ValidationError>().run { validateEmailWithRules(email).fix() } / ! @GopalAkshintala " overfullstack.github.io 43
  39. Controlling Strategy with Effect The Product fun <S> ValidatorAE<S, ValidationError>.validateEmailWithRules(email:

    String): Kind<S, Tuple2<String, StringC = tupledN( F Product contains(email, "@"), maxLength(email, 250) ).handleErrorWith { raiseError(it) } / ! @GopalAkshintala " overfullstack.github.io 44
  40. Controlling Strategy with Effect The Validations private fun <S> ValidatorAE<S,

    ValidationError>.contains(email: String, needle: String): Kind<S, String> = if (email.contains(needle, false)) just(email) else raiseError(ValidationError.DoesNotContain(needle).nel()) private fun <S> ValidatorAE<S, ValidationError>.maxLength(email: String, maxLength: Int): Kind<S, String> = if (email.length H maxLength) just(email) else raiseError(ValidationError.EmailMaxLength(maxLength).nel()) / ! @GopalAkshintala " overfullstack.github.io 45
  41. Please Feedback DM on twitter @GopalAkshintala Email me - [email protected]

    / ! @GopalAkshintala " overfullstack.github.io 47