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!
- https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0
- https://openhome.cc/eGossip/Blog/FunctionalProgrammingforJavaDevelopers4.html
- https://medium.com/@luijar/functional-programming-preserving-type-safety-ff4124837fc9
- https://www.youtube.com/watch?v=t1e8gqXLbsU
- https://www.youtube.com/watch?v=H4awPsyugS0
- https://www.youtube.com/watch?v=7SH3zWHdGoc
- https://www.educba.com/functional-programming-vs-oop/
- https://raygun.com/blog/costly-software-errors-history/
Resources
Are all our programs Dysfunctional?
By Shashank shekhar Dubey
Are all our programs Dysfunctional?
- 127