Programming Dysfunctionally?

So, how do we code?

String str = "Hello World";

void print_string(){

	printf (str);

}
int update_counter (int counter){
	
    counter ++;
    
    return counter;
}
int student_count = 0;

Student update_record (Student stu, int roll){
 	stu.roll_no = roll;
	student_count ++;
}

A closer look

class Student {
    int roll;
    
    String name;
}
int stu_count = 0;
void updateRecord (Student stu, int roll){
	
    String name = "";
    cin >> name;
    cout << "Name " << name << "Roll:" << roll;
    
    stu.roll = roll;
    stu.name = name;
    stu_count ++;
}

The functional in FP

$$y = f(x)$$

  • \(f(x)\) work only with the values provided to it (arguments).
  • \(f(x)\) doesn't do anything other than generating \(y\)
  • Replacing \(f(x)\) with some valid \(y\), should not make any difference.
  • given \(x\) to \(f\), it should always return \(y\).
void updateRecord (Student stu, int roll){
	
    String name = "";
    
    //side-effect
    cin >> name;                                
    
    //side-effect
    cout << "Name: " << name << "Roll: " << roll;
                                                
    stu.roll = roll;
    
    stu.name = name;
    
    //side-effect
    stu_count ++;
}

Remember those lines?

Think globally?

class Student {
    int roll;
    
    String name;
}
int stu_count = 0; // Global variable
  • Any function can change the state of Global Variables.
  • Tracking them is often difficult.
  • However, they do make programs more readable.
  • FP Solution: Keep the global variables, but don't allow reassignment.

Think globally?

Okay, so is there a rulebook?

The paradigm

  • Immutable variables
  • No side-effects
  • First-class and higher-order functions
  • Modularity
  • Lazy Evaluation
  • Monads

 

!! for pure functional !!

To mutate or not to?

int i = 5;
int j = 6;

int s = sum(i, j); // 11
i++;
s = sum(i, j); // 12

No \(i\)++ in functional programming.

Mutability

int i = 5;
int j = 6;

int s = sum(i, j); // 11

int i = i + 1;

int s = sum(i, j); // 12

Immutability

Immutability

  • Declaration with same name shadows the previous declaration in that scope.
  • State of the original variable (?) does not change.
  • Loops are not possible, use recursion.
  • Some FP languages gives a way to implement mutable variables, just in case.

Immutability: Challenges

  • Space overhead.
  • Garbage collector.
  • Handling complex data structures like array becomes costly.
  • Need persistent Data structures to optimize operations.

 

Side-effects

  • Modularize the code as much as possible.
  • Makes functions replacable.
  • Easy to extend, debug or optimize the code.

Avoiding Side-effects

fun increment(i){
	return i+1;
}

fun main(){
	int i = 5;
	int j = increment(i); //6
}
fun increment(i){
	return i+2;
}

1

2

fun increment(i){
	print("received value is %i");
	return i+1;
}

fun main(){
	int i = 5;
	int j = increment(i); //6
}

3

First-Class Functions

First-Class?

A function which can be used as a variable.

First-Class Functions

fun sum (a, b) {
    return (a+b)
}
var a = sum;
a (5,6); //11

Assignment

fun operate (operation, a, b){
    return operation (a, b);
}
operate (sum, 5, 6); //11

Passing as argument

First-Class Functions

Returning as a result

fun select_operator (index){
	
    fun sum (a,b) {return (a+b);}
    
    fun diff (a,b){return (a-b);}
    
    if(index == 1){
        return sum;
    }else{
        return diff;
    }
}
fun main(){
    cin >> index;
    
    fun a = select_operator(index);
    a(5,6);
    //do something
    a(6,7);
}

Higher Order Functions

A function which takes or returns another function

fun select_operator (index){
	
    if(index == 1){
        return (fun (a,b) => (a+b))
    }else{
        return (fun (a,b) => (a-b))
    }
}

Alternate representation with lambda operator

Higher Order Functions

Modularity

  • A way to organize code in FP.
  • Collection of functions that belong to single entity.
module List {
  type t
 
  fun add (list:t, newitem) {
      return (list + newitem);
  }

  fun remove (list:t, item){
      return (list - item);
  }

}
  • Signature (like Interface) abstracts the functions that are not to be exposed.

Modules

  • Real-life programs need side-effects.
  • Monads provide a way to do things with side-effect in FP.
  • Exceptions, null pointers, i/o, logging, non-determinism.
  • Converts/encapsulates the operands to a type which can handle side effects (like exception).
  • >>=

Monads

type Option = 
| Some x
| None
int i = None;
operate (i);

int i = Some (5);
operate (i);
  • Evaluates the expression at its first use.
  • Evaluation happens atmost once.
  • Stores the values to avoid repeated evaluation.
  • Causes book-keeping overhead.
  • Increases efficiency of the operations.
  • Useful to evaluate infinite data structures, like getting first 'x' prime numbers out of the list of infinite prime numbers.

Memoization...kind of !!!

Lazy Evaluation

result

f

f

f

3

4

4

3

//Normal Evaluation

result = f(f(a,b), f(a,b));
= f(f(3,4), f(a,b))
= f(7, f(a,b))
= f(7, f(3,4))
= f(7, 7)
= 14
f = sum;
a=3;
b=4;

result

f

f

3

4

//Lazy Evaluation

result = f(f(a,b), f(a,b));
= f(f(3,4), f(a,b))
= f(7, 7)
= 14
f = sum;
a=3;
b=4;
func evaluate (a, b){
    print (values are %a and %b);
    return a+b;
}

func prod(a,b){
    return a*b
}

func run (){
   return ( prod (evaluate (3,4), evaluate (3,4)) );
}

//output
values are 3 and 4
values are 3 and 4
49
Lazy func evaluate (a, b){
    print (values are %a and %b);
    return a+b;
}

func prod(a,b){
    return a*b
}

func run (){
   return ( prod (evaluate (3,4), evaluate (3,4)) );
}


//output
values are 3 and 4
49

OOP vs FP

OOP

  • Working entity is a value of an object
  • Related methods/ Functions are collected in Classes
  • Stateful flow of prog. with objects
  • Stateful => mutability
  • => order of execution matters 
  • Iteration & recursion both possible
  • Prefered when no. of entities are large

 

FP

  • Working entity is a value or a function
  • Related functions are collected in Modules    
  • Stateless flow of program   
  • Stateless => Immutability
  • => independent of order of execution
  • Iteration not possible in pure FP, recursion is used
  • Prefered when no. of operations are large
  • NASA's Mars Climate Orbiter was lost due to a miss of type conversion
  • Europe's satellite launching rocket crashed due to wrong data type usage (size wise)
  • Multiple flight crashes are attributed to programming errors
  • Mariner 1 space craft to Venus crashed back to Earth, due to a miss of hyphen in one of the instructions

A more thorough testing would have avoided many such failures, but is it that easy???

Just missed that ;

Testing becomes difficult when the program has :

  • type conversions
  • mutability (message passing)
  • parallel processing/concurrency

Just missed that ;

Its always nice to be careful, but a good programming paradigm would take care when you are not\(!^*\)

\(^*\)Some guy getting paid by FP people

So, what do we do with it?

  • MirageOS, a library OS, written in OCaml, is used in Unikernal for secure, high-performance network applications across a variety of cloud computing and mobile platforms.
  • Rust's original compiler was written in OCaml
  • WhatsApp is written in Erlang
  • Jane Street, one of the best proprietary trading firms uses OCaml as they find it more intuitive to understand by non-programming folks. Its easy to review codes for them.

FP in systems

  • FP as a paradigm goes as back as 1958, with LISP.
  • Being resource intensive, its use was discouraged.
  • With the advent of powerful systems and need to write complex and large code, functional programming is back in the game.

Not a spring chicken!

Resources