Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Functional Programming Patterns for the Pragmatic Programmer

In this talk we will see a pragmatic approach to building a purely functional architecture that delivers cohesive functional components.
We will cover functional patterns such as Free Monads, Transformers, Kleisli arrows, dependently typed checked exceptions
and types as well as how they can be glued together to achieve pure functions that are composable, context free, dependently injectable and testable.

Dome project and code with instructions to run it can be found at:

https://github.com/47deg/func-architecture

Related Books

Free with a 30 day trial from Scribd

See all

Functional Programming Patterns for the Pragmatic Programmer

  1. 1. Functional Programming Patterns (for the pragmatic programmer) ~ @raulraja CTO @47deg
  2. 2. Acknowledgment • Scalaz • Rapture : Jon Pretty • Miles Sabin : Shapeless • Rúnar Bjarnason : Compositional Application Architecture With Reasonably Priced Monads • Noel Markham : A purely functional approach to building large applications • Jan Christopher Vogt : Tmaps
  3. 3. Functions are first class citizens in FP Architecture
  4. 4. I want my main app services to strive for • Composability • Dependency Injection • Interpretation • Fault Tolerance
  5. 5. Composability Composition gives us the power to easily mix simple functions to achieve more complex workflows.
  6. 6. Composability We can achieve monadic function composition with Kleisli Arrows A M[B] In other words a function that for a given input it returns a type constructor… List[B], Option[B], Either[B], Task[B], Future[B]…
  7. 7. Composability When the type constructor M[_] it's a Monad it can be composed and sequenced in for comprehensions val composed = for { a <- Kleisli((x : String) Option(x.toInt + 1)) b <- Kleisli((x : String) Option(x.toInt * 2)) } yield a + b
  8. 8. Composability The deferred injection of the input parameter enables Dependency Injection val composed = for { a <- Kleisli((x : String) Option(x.toInt + 1)) b <- Kleisli((x : String) Option(x.toInt * 2)) } yield a + b composed.run("1")
  9. 9. Composability : Kleisli What about when the args are not of the same type? val composed = for { a <- Kleisli((x : String) Option(x.toInt + 1)) b <- Kleisli((x : Int) Option(x * 2)) } yield a + b
  10. 10. Composability : Kleisli By using Kleisli we just achieved • Composability • Dependency Injection • Interpretation • Fault Tolerance
  11. 11. Interpretation : Free Monads What is a Free Monad? -- A monad on a custom ADT that can be run through an Interpreter
  12. 12. Interpretation : Free Monads sealed trait Op[A] case class Ask[A](a: () A) extends Op[A] case class Async[A](a: () A) extends Op[A] case class Tell(a: () Unit) extends Op[Unit]
  13. 13. Interpretation : Free Monads What can you achieve with a custom ADT and Free Monads? def ask[A](a: A): OpMonad[A] = Free.liftFC(Ask(() a)) def async[A](a: A): OpMonad[A] = Free.liftFC(Async(() a)) def tell(a: Unit): OpMonad[Unit] = Free.liftFC(Tell(() a))
  14. 14. Interpretation : Free Monads Functors and Monads for Free (No need to manually implement map, flatMap, etc...) type OpMonad[A] = Free.FreeC[Op, A] implicit val MonadOp: Monad[OpMonad] = Free.freeMonad[({type λ[α] = Coyoneda[Op, α]})#λ]
  15. 15. Interpretation : Free Monads At this point a program like this is nothing but Data describing the sequence of execution but FREE of it's runtime interpretation. val program = for { a <- ask(1) b <- async(2) _ <- tell(println("log something")) } yield a + b
  16. 16. Interpretation : Free Monads We isolate interpretations via Natural transformations AKA Interpreters. In other words with map over the outer type constructor Op object ProdInterpreter extends (Op ~> Task) { def apply[A](op: Op[A]) = op match { case Ask(a) Task(a()) case Async(a) Task.fork(Task.delay(a())) case Tell(a) Task.delay(a()) } }
  17. 17. Interpretation : Free Monads We can have different interpreters for our production / test / experimental code. object TestInterpreter extends (Op ~> Id.Id) { def apply[A](op: Op[A]) = op match { case Ask(a) a() case Async(a) a() case Tell(a) a() } }
  18. 18. Requirements • Composability • Dependency Injection • Interpretation • Fault Tolerance
  19. 19. Fault Tolerance Most containers and patterns generalize to the most common super-type or simply Throwable loosing type information. val f = scala.concurrent.Future.failed(new NumberFormatException) val t = scala.util.Try(throw new NumberFormatException) val d = for { a <- 1.right[NumberFormatException] b <- (new RuntimeException).left[Int] } yield a + b
  20. 20. Fault Tolerance We don't have to settle for Throwable!!! We could use instead… • Nested disjunctions • Coproducts • Delimited, Monadic, Dependently-typed, Accumulating Checked Exceptions
  21. 21. Fault Tolerance : Dependently- typed Acc Exceptions Introducing rapture.core.Result
  22. 22. Fault Tolerance : Dependently- typed Acc Exceptions Result is similar to / but has 3 possible outcomes (Answer, Errata, Unforeseen) val op = for { a <- Result.catching[NumberFormatException]("1".toInt) b <- Result.errata[Int, IllegalArgumentException]( new IllegalArgumentException("expected")) } yield a + b
  23. 23. Fault Tolerance : Dependently- typed Acc Exceptions Result uses dependently typed monadic exception accumulation val op = for { a <- Result.catching[NumberFormatException]("1".toInt) b <- Result.errata[Int, IllegalArgumentException]( new IllegalArgumentException("expected")) } yield a + b
  24. 24. Fault Tolerance : Dependently- typed Acc Exceptions You may recover by resolving errors to an Answer. op resolve ( each[IllegalArgumentException](_ 0), each[NumberFormatException](_ 0), each[IndexOutOfBoundsException](_ 0))
  25. 25. Fault Tolerance : Dependently- typed Acc Exceptions Or reconcile exceptions into a new custom one. case class MyCustomException(e : Exception) extends Exception(e.getMessage) op reconcile ( each[IllegalArgumentException](MyCustomException(_)), each[NumberFormatException](MyCustomException(_)), each[IndexOutOfBoundsException](MyCustomException(_)))
  26. 26. Requirements We have all the pieces we need Let's put them together! • Composability • Dependency Injection • Interpretation • Fault Tolerance
  27. 27. Solving the Puzzle How do we assemble a type that is: Kleisli + Custom ADT + Result for { a <- Kleisli((x : String) ask(Result.catching[NumberFormatException](x.toInt))) b <- Kleisli((x : String) ask(Result.catching[IllegalArgumentException](x.toInt))) } yield a + b We want a and b to be seen as Int but this won't compile because there are 3 nested monads
  28. 28. Solving the Puzzle : Monad Transformers Monad Transformers to the rescue! type ServiceDef[D, A, B <: Exception] = ResultT[({type λ[α] = ReaderT[OpMonad, D, α]})#λ, A, B]
  29. 29. Solving the Puzzle : Services Two services with different dependencies case class Converter() { def convert(x: String): Int = x.toInt } case class Adder() { def add(x: Int): Int = x + 1 } case class Config(converter: Converter, adder: Adder) val system = Config(Converter(), Adder())
  30. 30. Solving the Puzzle : Services Two services with different dependencies def service1(x : String) = Service { converter: Converter ask(Result.catching[NumberFormatException](converter.convert(x))) } def service2 = Service { adder: Adder ask(Result.catching[IllegalArgumentException](adder.add(22) + " added ")) }
  31. 31. Solving the Puzzle : Services Two services with different dependencies val composed = for { a <- service1("1").liftD[Config] b <- service2.liftD[Config] } yield a + b composed.exec(system)(TestInterpreter) composed.exec(system)(ProdInterpreter)
  32. 32. Conclusion • Composability : Kleisli • Dependency Injection : Kleisli • Interpretation : Free monads • Fault Tolerance : Dependently typed checked exceptions
  33. 33. Thanks! @raulraja @47deg http://github.com/47deg/func-architecture

    Be the first to comment

    Login to see the comments

  • kevinkyyro

    Oct. 8, 2015
  • vicchugu

    Oct. 16, 2015
  • mooling

    Oct. 19, 2015
  • matteomanco

    Nov. 1, 2015
  • MustafaKarabat

    Nov. 2, 2015
  • dxhuy88

    Nov. 12, 2015
  • tantrieuf31

    Nov. 12, 2015
  • IvanZivanovic4

    Dec. 8, 2015
  • Pimpona77

    Jan. 5, 2016
  • tminami

    Jan. 30, 2016
  • ShingoSuzuki1

    Jan. 30, 2016
  • yutosuzu

    Jan. 30, 2016
  • PatrickMadden3

    Feb. 18, 2016
  • vershatrivedi

    Apr. 12, 2016
  • teknoShin

    Jun. 4, 2016
  • ssuser74bf30

    Oct. 3, 2016
  • PierangeloCecchetto

    Nov. 8, 2016
  • TabarekAyad

    Mar. 23, 2017
  • SameerHawsawi

    Sep. 12, 2017
  • KrzysiekKondracki

    Jul. 18, 2018

In this talk we will see a pragmatic approach to building a purely functional architecture that delivers cohesive functional components. We will cover functional patterns such as Free Monads, Transformers, Kleisli arrows, dependently typed checked exceptions and types as well as how they can be glued together to achieve pure functions that are composable, context free, dependently injectable and testable. Dome project and code with instructions to run it can be found at: https://github.com/47deg/func-architecture

Views

Total views

7,755

On Slideshare

0

From embeds

0

Number of embeds

821

Actions

Downloads

104

Shares

0

Comments

0

Likes

32

×