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

Functional vs Object-Oriented Programming

Functional vs Object-Oriented Programming

Wondering what are the key differences between functional and object-oriented programming? Look no further - this deck highlights the core features of both approaches.
Additional information is also included on the yellow post-its.

Wojciech Ptak

March 24, 2016
Tweet

Other Decks in Technology

Transcript

  1. Functional vs Object- Oriented Programming EvoTalk, 24.03.2016 If you happen

    to read this deck yourself (as opposed to having it presented), these yellow stickers are for you! They contain crucial information that shouldn’t clutter the slides during regular presentation.
  2. Functional vs Object- Oriented Programming EvoTalk, 24.03.2016 This is a

    terrible title. Functional Programming and Object-Oriented Programming are two things of different types - they are not opposite! If you happen to read this deck yourself (as opposed to having it presented), these yellow stickers are for you! They contain crucial information that shouldn’t clutter the slides during regular presentation.
  3. Top-level Programming Paradigms Imperative Functional Logic var line := readLine();

    var x := parseInt(line); var x2 := x*x; printLine(x2); pow2 :: Int -> Int pow2 x = x*x pow4 :: Int -> Int pow4 = pow2 . pow2 higher(A, B) :- higher(A, C), higher(C, B). higher(tom, sue). higher(sue, jenny). ? higher(X, jenny)
  4. Top-level Programming Paradigms Imperative Functional Logic var line := readLine();

    var x := parseInt(line); var x2 := x*x; printLine(x2); pow2 :: Int -> Int pow2 x = x*x pow4 :: Int -> Int pow4 = pow2 . pow2 higher(A, B) :- higher(A, C), higher(C, B). higher(tom, sue). higher(sue, jenny). ? higher(X, jenny) Bonus: Prolog
  5. Top-level Programming Paradigms Imperative Functional Logic These are the “three

    kings”. Any other paradigms are adapted inside the existing ones. Procedural programming, for example, assumes the use of procedures as opposed to jumps - but it makes sense only within the imperative paradigm. Event driven paradigm, on the other side, assumes programming responses to events - but these have to be expressed in either imperative, functional, or logical way. var line := readLine(); var x := parseInt(line); var x2 := x*x; printLine(x2); pow2 :: Int -> Int pow2 x = x*x pow4 :: Int -> Int pow4 = pow2 . pow2 higher(A, B) :- higher(A, C), higher(C, B). higher(tom, sue). higher(sue, jenny). ? higher(X, jenny)
  6. Functional Programming ? Approach that treats computation as the evaluation

    of mathematical functions and avoids changing state and mutable data
  7. Functional Programming ? Approach that treats computation as the evaluation

    of mathematical functions and avoids changing state and mutable data Pure functions Referential transparency
  8. Functional Programming ? Approach that treats computation as the evaluation

    of mathematical functions and avoids changing state and mutable data Pure functions Referential transparency A function is pure if it satisfies two conditions: 1. It depends only on the arguments (as opposed to some global variables) 2. It does not mutate anything and do not cause side-effects If any function F is pure, these two snippets:: 1. Let x = F(2); let y = F(2); 2. Let x = F(2); let y = x; Are identical. The possibility to replace a function call with its result from earlier invocation is called referential transparency.
  9. Functional Programming ? Approach that treats computation as the evaluation

    of mathematical functions and avoids changing state and mutable data Pure functions Referential transparency type Employee = (String, Double) giveRaise :: Percentage -> Employee -> Employee giveRaise p (name, salary) = (name, salary * (1+percentage/100)
  10. Functional Programming ? Pure functions x => x*2 { x

    => log.info(x) x*2 } a => cache(a).getOrElse(f(a)) a => cache.getOrUpdate(a, f(a)) a => db.store(a)
  11. Functional Programming ? Pure functions x => x*2 { x

    => log.info(x) x*2 } a => cache(a).getOrElse(f(a)) a => cache.getOrUpdate(a, f(a)) a => db.store(a) The first function is definitely pure - it has no side-effects at all. The last one saves an element in the database, which is pretty as much effectful as it gets. The three in the middle, though, are problematic: does logging some information count as a side-effect? Haskell programmers would say “yes”, but does it really help? If you can accept that logging might be considered pure, what about checking cache? Storing in cache? What if the cache is in fact a DB and accessed via network? These questions have no strict answers.
  12. SIMULA Discrete event systems Domain objects • Classes • Interfaces

    • Inheritance Simula is a programming language that was created around 1961 for a special purpose: running simulations of event systems (think assembly lines in the factories). It is based on ALGOL 60 and is a first example of production-ready object-oriented language.
  13. SIMULA Discrete event systems Domain objects • Classes • Interfaces

    • Inheritance Subtype Polymorphism Late Binding Virtual Methods
  14. SIMULA Discrete event systems Domain objects • Classes • Interfaces

    • Inheritance Subtype Polymorphism Late Binding Virtual Methods Originally, objects accept messages. The exact piece of code that will be called to interpret a message depends on the concrete type of the receiver. So a caller may know that he is sending message “validateAndExecute” to an object that represents some kind of money transaction. Since the transaction in question is in fact a bitcoin deal, this will result in some crypto-magic stuff happening. The way that concrete implementation is assigned in the run time (not during compilation) is called late binding of methods.
  15. Subtype Polymorphism ? Bonus: how vtables work? trait PaymentTransaction {

    Def validateAndExecute(): Boolean } val transaction: PaymentTransaction = ??? Val succeeded = transaction.validateAndExecute()
  16. FP OOP Goals Make reasoning easier by eliminating mutable state

    and implicit dependencies on global variables. Make maintenance easier by aligning the program structure with the domain structure.
  17. FP Make reasoning easier by eliminating mutable state and implicit

    dependencies on global variables. The code is easier to read with no-mutation guarantee. If one knows value of “x” at any point of time, they can be sure it had the same value before and will not change afterwards. Hard-core functional languages in fact have no global variables - because they have no variables at all! They only have labels, which are in fact named values. A label must have a value assigned right away and cannot be changed. This also means no for or while loops, because there is nothing to loop over.
  18. OOP Make maintenance easier by aligning the program structure with

    the domain structure. The main assumption behind OOP is that whenever requirements change, it affects only a small number of domain objects. So as long as the structure of the program resembles the structure of the domain, the changes will be easy to implement. The users (clients) are rarely programmers, so it’s difficult to talk about requirements for software. They do, however, know their domains. OOP may help with translating one to another.
  19. FP: Algebraic Data Types An Algebraic Data Type is created

    by algebraic operations: sum and product Data BlogEntry = Text String | Photo Image String
  20. FP: Algebraic Data Types An Algebraic Data Type is created

    by algebraic operations: sum and product The example here is Haskell code. It defines a new type, called “BlogEntry”, that is a sum. It means that is is either a “Text String” or “Photo Image String”, but not both. “Text String” is one variant, called “Text”, and represented as String. The other variant, called “Photo”, is represented as product of Image and String - meaning it has both an image and a caption. Data BlogEntry = Text String | Photo Image String
  21. FP: Algebraic Data Types An Algebraic Data Type is created

    by algebraic operations: sum and product Types are described by the sort of data they contain Data BlogEntry = Text String | Photo Image String The example here is Haskell code. It defines a new type, called “BlogEntry”, that is a sum. It means that is is either a “Text String” or “Photo Image String”, but not both. “Text String” is one variant, called “Text”, and represented as String. The other variant, called “Photo”, is represented as product of Image and String - meaning it has both an image and a caption.
  22. FP: Pattern Matching Comment :: BlogEntry -> IO () Comment

    entry = case entry of Text content -> … Photo image string -> ... Functions can destructure the ADT back into pieces and process them edit :: BlogEntry -> String -> BlogEntry Edit e newtext = case e of Text content -> Text newtext Photo image string -> Photo image newtext Data BlogEntry = Text String | Photo Image String
  23. OOP: Objects class BlogEntry: Def toHTML(self): ... Def comment(self, author,

    comment): ... Types are described by messages they understand
  24. TextBlogEntry PhotoBlogEntry archive comment ? Bonus: Expression Problem ? ?

    ? The Expression Problem: How to structure the code so that further users can add both new constructors (types) and new functionalities without recompiling the existing pieces? It is a well known tool used to asses the expressive power of a language. It has multiple solution for both functional and object-oriented approaches, all of which are rather cumbersome.
  25. OOP: More Objects class Algebra() { ... } class Stats(algebra:

    Algebra) { ... } class Math { val algebra = new Algebra() val stats = new Stats(algebra) val fun = new FunctionAnalysis() ... } val math = new Math() val data: List[Int] = ... val median = math.stats.median(data)
  26. OOP: More Objects class Algebra() { ... } class Stats(algebra:

    Algebra) { ... } class Math { val algebra = new Algebra() val stats = new Stats(algebra) val fun = new FunctionAnalysis() ... } val math = new Math() val data: List[Int] = ... val median = math.stats.median(data) Even if a particular language does not provide a module system, you can easily roll out your own using just classes. In this example a class “Math” collects all the submodules for mathematical computations. It is enough for you to hand your user the “Math” object - then all the submodules (encoded as objects) will be available via the main object methods. Therefore, you get namespacing for free! Having circular dependencies sucks, though.
  27. Classy OOP: Upcasting class CoffeeMaker { def makeCoffee(withSugar: Boolean): Coffee

    = { ... } } class CountingCoffeeMaker extends CoffeeMaker { private var coffeesSoFar = 0 override def makeCoffee(withSugar: Boolean): Coffee = { coffeesSoFar += 1 return super.makeCoffee(withSugar) } def getCoffiesSoFar(): Int { Return coffeesSoFar } } val cm: CoffeeMaker = new CountingCoffeeMaker()
  28. Classy OOP: Upcasting class CoffeeMaker { def makeCoffee(withSugar: Boolean): Coffee

    = { ... } } class CountingCoffeeMaker extends CoffeeMaker { private var coffeesSoFar = 0 override def makeCoffee(withSugar: Boolean): Coffee = { coffeesSoFar += 1 return super.makeCoffee(withSugar) } def getCoffiesSoFar(): Int { Return coffeesSoFar } } val cm: CoffeeMaker = new CountingCoffeeMaker() Here, an instance of “CountingCoffeeMaker” is explicitly typed as “CoffeeMaker”. This is a technique that allows to hide implementation details from the library users.
  29. Modules Typeclasses Various functional languages feature different module systems. Haskell,

    for example, has a very weak module system: a module is just a bag of types and functions. The types in a module may at least be abstract - meaning you cannot see their contents and can only operate on them using the functions provided in the module. SML family, however, has a really strong module system. The modules are well defined entities, they have separate interfaces and implementations, and you can even write functions that transform modules of one type into another! A typeclass serves as a proof that a certain type can do something. For example there is a “Show a” typeclass in Haskell which asserts that value of type “a” can be converted to strings. Then there are multiple instances of this typeclass: “Show string” (trivial one), “Show Int”, and more derived, like “Show a => Show [a]”, which reads “for any type <a>, if there is a <Show a> instance, there is also <Show [a]> instance” (“[a]” is the type of lists with elements of type “a”). So if you’re writing a logger, you don’t need to know the exact type of what you’re logging - it’s enough if you know it provides a “Show” instance.
  30. OOP: No problemo. Modularity / Abstraction FP: (puts down the

    duct tape) Sure, works! Typeclasses + Typeclasses are cool.
  31. Impure and Eager Quite a lot of modern programming languages

    can be viewed as “object- oriented with functional possibilities”. The most famous for combining OOP and FP is probably Scala - but there are lots of others, like Java, C#, F#, Go, Python, JavaScript, TypeScript, OCaml, and much more! They are all eager and impure (so disregarded by “truly FP” community), because it was an easy transition from just-OOP to OOP-with-lambdas- and-higher-kinds. The greatest challenge these days is creating the good typechecker - the amount of flexibility and abstraction needed for FP typing (higher kinds, preferably also dependant types) when mixed with complexity of OOP typing (subclassing, interfaces, variance) is a tough nut to crack.