Friendly Functional Programming

Programming is about solving real world problems, each program describes the different instructions, actions and events “What to do if..?” to deal with some problems.

Programming is like creating a life style following some rules which are defined by the programmer. We might be the author of the life story of someone who trusted us to make their life easier.

So we need to find a good destination, laws to follow, a new way of thinking, a right way to make our life and life of others better.

In imperative paradigm we get things done by giving a sequence of tasks and then execute them, in this blog we will learn another programming paradigm “Functional Programming”.

Functional programming

Functional programming is a beautiful declarative programming paradigm that can make us confident about our code. It separates between data and its functionality.

In FP we implement purely functional code to describe “what is this action?” in every step, it’s like we will create the story of each task in the form of functions.

Let’s get started!

The Data structures are like people or objects that have some behaviors:

case class Person(name: String, state: State)
sealed trait State
case object Happy extends State
case object Ok extends State //WARNING
case object Sad extends State
val value: String = "Hello"

in FP we call those Data Structures: Algebraic Data Structure (ADT):

  • Person here is a product type: Person has name AND state
  • State is a sum type that contains a value of one of the those types: HappyOR Ok OR Sad
  • The simple types are called values

Functions change the behavior of its input (ADTs)

In order to write pure functions we have to make sure that our function is:

  • Total: You get what you expect, the return type should be honest.

Don’t do that!

def function(): 😇 = { 😈}

Hey function(), be honest about what you’re doing!

def function(): 😇 = { 😇}
def function(): 😈 = { 😈}
  • Deterministic: for a given input, you will get the same output

Each Coffee capsule has its own taste, when you put the same capsule many times you will get always the same taste without surprise.

A non-deterministic function in this case would produce this result:

  • Free of side effect: compute the function result from the given inputs without doing anything else. The function should do only its expected job without interacting with the outside world besides returning a value.

Don’t do that!

def makeFriends(persons: List[Person], behaviors: Set[Behavior]): List[Person] = {
 val friends: List[Person] = 
 persons.collect { case p if p.behavior.intersect(behaviors).size ==   
   behaviors.size => p }
  this.kill //😱

Do that:

def makeFriends(persons: List[Person], behaviors: Set[Behavior]): List[Person] = 
persons.collect { case p if p.behavior.intersect(behaviors).size == behaviors.size => p }

Referential transparency

If your function is deterministic and free of side effects, it is transparent. A function is pure if the expression function(x) is referentially transparent for all x. Which means we can replace the function(x) by the result of its evaluation without affecting the meaning of our program.

For example:

def addOne(x: Int) = x + 1

10 + 1 is an expression that applies the pure function addOne to the value 10 the evaluation of this function can be replaced by 11 without changing the meaning of our program.

Every time we can ensure that addOne(10) == 11

Function composition

In order to perform computations, in FP we combine two or more functions together. We create a composable programs using FP approach.

We perform different operations for a given input to produce different values.

In FP, we need to know about the whole story of our ADT behaviors. We deal with immutable Data Structures and we extract new data from existing one using compositions to make the scenario of the stories.

Example: The mathematic operations are functions, we use the result of the addition and then multiply it by 10.

    x = (14 + 23) * 10
val x = mult(add(14, 23), 10)

First class functions : Higher-order-functions

In FP, you can pass functions as arguments in other functions and you can store them in Data Structures. You can define values of type function.

Sometimes we call: list(-1, 3).filter(_ > 0) => _ > 0 = a => a > 0 is actually an anonymous function “Lambda” : Int => Boolean we created a function without naming it, we can also define a function and apply it for each element of the List like this:

def verify(a: Int): Boolean = a > 0
List(-1, 3).filter(a => verify(a))

filter is a higher order function, because it takes a function as a parameter.

Lazy evaluation

In FP the computations are lazy which means the function is executed only when that value is itself needed for some further computation. The function will be called only if it’s really forced to show you a result.

Recursive functions

In FP, we use recursive functions to loop over the Data Structures or to evaluate a computations until it reaches the base cases (terminating condition). Recursive functions may throw StackoverFlowException, we can avoid that and implement tail recursion instead to perform the calculation first and at the end we call the recursive function.

Source: medium