Slide 1

Slide 1 text

Functional vs Object- Oriented Programming EvoTalk, 24.03.2016

Slide 2

Slide 2 text

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.

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

Origins: paradigms

Slide 5

Slide 5 text

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)

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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)

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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.

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

Functional Programming ? Pure functions Bonus: where monads come from. denotational semantics

Slide 15

Slide 15 text

Origins: OOP

Slide 16

Slide 16 text

SIMULA Discrete event systems Domain objects ● Classes ● Interfaces ● Inheritance

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

SIMULA Discrete event systems Domain objects ● Classes ● Interfaces ● Inheritance Subtype Polymorphism Late Binding Virtual Methods

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

Subtype Polymorphism ? Bonus: how vtables work? trait PaymentTransaction { Def validateAndExecute(): Boolean } val transaction: PaymentTransaction = ??? Val succeeded = transaction.validateAndExecute()

Slide 21

Slide 21 text

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.

Slide 22

Slide 22 text

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.

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

Small Building blocks

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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.

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

OOP: Objects class BlogEntry: Def toHTML(self): ... Def comment(self, author, comment): ... Types are described by messages they understand

Slide 30

Slide 30 text

TextBlogEntry PhotoBlogEntry archive comment Class Class Object-Oriented Programming

Slide 31

Slide 31 text

TextBlogEntry PhotoBlogEntry archive comment Function Functional Programming Function

Slide 32

Slide 32 text

TextBlogEntry PhotoBlogEntry archive comment ? Bonus: Expression Problem ? ? ?

Slide 33

Slide 33 text

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.

Slide 34

Slide 34 text

Big Building Blocks

Slide 35

Slide 35 text

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)

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

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()

Slide 38

Slide 38 text

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.

Slide 39

Slide 39 text

FP: ? Modules Typeclasses

Slide 40

Slide 40 text

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 , if there is a instance, there is also 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.

Slide 41

Slide 41 text

OOP: No problemo. Modularity / Abstraction FP: (puts down the duct tape) Sure, works! Typeclasses + Typeclasses are cool.

Slide 42

Slide 42 text

What If I told you Objective-Oriented Functional Programming is not a lie?

Slide 43

Slide 43 text

Choose wisely Pure And lazy Impure and Eager

Slide 44

Slide 44 text

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.

Slide 45

Slide 45 text

x.(new T(x))