1567219092
There is a lot of hype around functional programming (FP) and a lot of cool kids are doing it but it is not a silver bullet. Like other programming paradigms/styles, functional programming also has its pros and cons and one may prefer one paradigm over the other. If you are a TypeScript/JavaScript developer and wants to venture into functional programming, do not worry, you don't have to learn functional programming oriented languages like Haskell or Clojure since JavaScript and hence TypeScript has you covered and this post is for you.
If you are looking for functional programming in Java or Golang check other posts in the series.
I'm not gonna dive into all functional programming concepts in detail, instead, I'm gonna focus on things that you can do in TypeScript which are in line with functional programming concepts. I'm also not gonna discuss the pros and cons of functional programming in general.
Please keep in mind, though this post is about TypeScript, you can easily do the same in JavaScript as well since TypeScript is just a typed superset of JavaScript.
As per Wikipedia,
Functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
Hence in functional programming, there are two very important rules
This means:
Apart from these there are functional programming concepts below that can be applied in TypeScript, we will touch upon these further down.
Using functional programming doesn't mean its all or nothing, you can always use functional programming concepts to complement Object-oriented concepts in TypeScript. The benefits of functional programming can be utilized whenever possible regardless of the paradigm or language you use. And that is exactly what we are going to see.
TypeScript is not a purely functional language but offers a lot of concepts which are in line with functional languages, so let us see how we can apply some of the functional programming concepts above in TypeScript.
First-class functions(function as a first-class citizen) means you can assign functions to variables, pass a function as an argument to another function or return a function from another. TypeScript supports this and hence makes concepts like closures, currying, and higher-order-functions easy to write.
A function can be considered as a higher-order-function only if it takes one or more functions as parameters or if it returns another function as a result.
In TypeScript, this is quite easy to do
type mapFn = (it: string) => number;// The higher-order-function takes an array and a function as arguments
function mapForEach(arr: string[], fn: mapFn): number[] {
const newArray: number[] = [];
arr.forEach(it => {
// We are executing the method passed
newArray.push(fn(it));
});
return newArray;
}const list = [“Orange”, “Apple”, “Banana”, “Grape”];
// we are passing the array and a function as arguments to mapForEach method.
const out = mapForEach(list, (it: string): number => it.length);console.log(out); // [6, 5, 6, 5]
But then in JavaScript/TypeScript we could also simply do it this way using built-in functional methods like map, reduce and so on.
const list = [“Orange”, “Apple”, “Banana”, “Grape”];// we are passing a function as arguments to the built-in map method.
const out = list.map(it => it.length);console.log(out); // [6, 5, 6, 5]
Closures and currying are also possible in TypeScript
// this is a higher-order-function that returns a function
function add(x: number): (y: number) => number {
// A function is returned here as closure
// variable x is obtained from the outer scope of this method and memorized in the closure
return (y: number): number => x + y;
}// we are currying the add method to create more variations
var add10 = add(10);
var add20 = add(20);
var add30 = add(30);console.log(add10(5)); // 15
console.log(add20(5)); // 25
console.log(add30(5)); // 35
There are also many built-in declarative higher-order-functions in TypeScript/JavaScript like map
, reduce
, forEach
, filter
and so on. There are also many libraries that provide functional interfaces to be used in TypeScript/JavaScript.
As we saw already a pure function should return values only based on the arguments passed and should not affect or depend on global state. It is possible to do this in TypeScript easily.
This is quite simple, take the below this is a pure function. It will always return the same output for the given input and its behavior is highly predictable. We can safely cache the method if needed.
function sum(a: number, b: number): number {
return a + b;
}
If we add an extra line in this function, the behavior becomes unpredictable as it now has a side effect that affects an external state.
const holder = {};function sum(a: number, b: number): number {
let c = a + b;
holder[${a}+${b}
] = c;
return c;
}
So try to keep your functions pure and simple. Using tools like ESLint and typescript-eslint it is possible to enforce these.
Functional programming favors recursion over looping. Let us see an example for calculating the factorial of a number.
In traditional iterative approach:
function factorial(num: number): number {
let result = 1;
for (; num > 0; num–) {
result *= num;
}
return result;
}console.log(factorial(20)); // 2432902008176640000
The same can be done using recursion as below which is favored in functional programming.
const factorial = (num: number): number =>
num == 0 ? 1 : num * factorial3(num - 1);console.log(factorial(20)); // 2432902008176640000
The downside of the recursive approach is that it will be slower compared to an iterative approach most of the times(The advantage we are aiming for is code simplicity and readability) and might result in stack overflow errors since every function call needs to be saved as a frame to the stack. To avoid this tail recursion is preferred, especially when the recursion is done too many times. In tail recursion, the recursive call is the last thing executed by the function and hence the functions stack frame need not be saved by the compiler. Most compilers can optimize the tail recursion code the same way iterative code is optimized hence avoiding the performance penalty. Tail call optimization is part of the ECMAScript specs but unfortunately, most JavaScript engines do not support this yet.
Now using tail recursion the same function can be written as below, but depending on the engine this might not be optimized, though there are workarounds, still it performed better in benchmarks.
const factorialTailRec = (num: number): number => factorial(1, num);const factorial = (accumulator: number, val: number): number =>
val == 1 ? accumulator : factorial(accumulator * val, val - 1);console.log(factorialTailRec(20)); // 2432902008176640000
Consider using recursion when writing TypeScript code for readability and immutability, but if performance is critical or if the number of iterations will be huge use standard loops.
Lazy evaluation or non-strict evaluation is the process of delaying evaluation of an expression until it is needed. In general, TypeScript does strict/eager evaluation but for operands like &&
, ||
and ?:
it does a lazy evaluation. We can utilize short-circuiting, higher-order-functions, closures, and memoization techniques to do lazy evaluations.
Take this example where TypeScript eagerly evaluates everything.
function add(x: number): number {
console.log(“executing add”); // this is printed since the functions are evaluated first
return x + x;
}function multiply(x: number): number {
console.log(“executing multiply”); // this is printed since the functions are evaluated first
return x * x;
}function addOrMultiply(
add: boolean,
onAdd: number,
onMultiply: number
): number {
return add ? onAdd : onMultiply;
}console.log(addOrMultiply(true, add(4), multiply(4))); // 8
console.log(addOrMultiply(false, add(4), multiply(4))); // 16
This will produce the below output and we can see that both functions are executed always
executing add
executing multiply
8
executing add
executing multiply
16
We can use higher-order-functions to rewrite this into a lazily evaluated version
function add(x: number): number {
console.log(“executing add”);
return x + x;
}function multiply(x: number): number {
console.log(“executing multiply”);
return x * x;
}type fnType = (t: number) => number;
// This is now a higher-order-function hence evaluation of the functions are delayed in if-else
function addOrMultiply(
add: boolean,
onAdd: fnType,
onMultiply: fnType,
t: number
): number {
return add ? onAdd(t) : onMultiply(t);
}
console.log(addOrMultiply(true, add, multiply, 4));
console.log(addOrMultiply(false, add, multiply, 4));
This outputs the below and we can see that only required functions were executed
executing add
8
executing multiply
16
Or by memoization like this
const cachedAdded = {};
function add(x: number): number {
if (cachedAdded[x]) {
return cachedAdded[x];
}
console.log(“executing add”);
const out = x + x;
cachedAdded[x] = out;
return out;
}const cachedMultiplied = {};
function multiply(x: number): number {
if (cachedMultiplied[x]) {
return cachedMultiplied[x];
}
console.log(“executing multiply”);
const out = x * x;
cachedMultiplied[x] = out;
return out;
}function addOrMultiply(
add: boolean,
onAdd: number,
onMultiply: number
): number {
return add ? onAdd : onMultiply;
}console.log(addOrMultiply(true, add(4), multiply(4))); // 8
console.log(addOrMultiply(false, add(4), multiply(4))); // 16
This outputs the below and we can see that functions were executed only once for the same values
executing add
executing multiply
8
16
Please note that memoization techniques will work only when your functions are pure and referentially transparent.
There are also other ways of doing Lazy evaluations like this. Doing Lazy evaluations in TypeScript might not be worth the code complexity some of the times, but if the functions in question are heavy in terms of processing then its is absolutely worth it to lazy evaluate them.
TypeScript has a strong type system and also has great type inference. While the underlying JavaScript itself is weakly typed, TypeScript along with a compatible IDE can bridge that gap.
From Wikipedia:
Functional programs do not have assignment statements, that is, the value of a variable in a functional program never changes once defined. This eliminates any chances of side effects because any variable can be replaced with its actual value at any point of execution. So, functional programs are referentially transparent.
Unfortunately, there are not many ways to strictly limit data mutation in TypeScript, however by using pure functions and by explicitly avoiding data mutations and reassignment using other concepts we saw earlier this can be achieved. TypeScript by default passes primitive variables by value and objects by reference so we need to take care not to mutate data inside functions. Libraries like Immutable JS could also be considered. Use const
as much as possible to avoid reassignments.
For example, the below will produce an error
const list = [“Apple”, “Orange”, “Banana”, “Grape”];list = [“Earth”, “Saturn”];
But this will not help when variables are holding references to other objects, for example, the below mutation will work irrespective of the const
keyword.
const list = [“Apple”, “Orange”, “Banana”, “Grape”];list.push(“Earth”);
list.push(“Saturn”);
const
keyword allows the internal state of referenced variables to be mutated and hence from a functional programming perspective const
keyword is useful only for primitive constants and to catch reassignments.
Other techniques to follow are using built-in methods like map, reduce, filter and so on as they do not mutate the data. We can also use this ESlint plugin to restrict mutations.
When using functional programming techniques it is encouraged to use functional data types such as Stacks, Maps and Queues.
Hence maps are better than arrays or hash sets in functional programming as data stores.
This is just an introduction for those who are trying to apply some functional programming techniques in TypeScript. There are a lot more that can be done in TypeScript and with the ever-evolving ECMAScript underneath, this should be even easier. As I said earlier functional programming is not a silver bullet but it offers a lot of useful techniques for more understandable, maintainable and testable code. It can co-exist perfectly well with imperative and object-oriented programming styles. In fact, we all should be using the best of everything.
If you like this article, please leave a like or a comment and share it with all of your programming buddies!
Originally published on dev.to
#typescript #javascript #function #web-development
1567219092
There is a lot of hype around functional programming (FP) and a lot of cool kids are doing it but it is not a silver bullet. Like other programming paradigms/styles, functional programming also has its pros and cons and one may prefer one paradigm over the other. If you are a TypeScript/JavaScript developer and wants to venture into functional programming, do not worry, you don't have to learn functional programming oriented languages like Haskell or Clojure since JavaScript and hence TypeScript has you covered and this post is for you.
If you are looking for functional programming in Java or Golang check other posts in the series.
I'm not gonna dive into all functional programming concepts in detail, instead, I'm gonna focus on things that you can do in TypeScript which are in line with functional programming concepts. I'm also not gonna discuss the pros and cons of functional programming in general.
Please keep in mind, though this post is about TypeScript, you can easily do the same in JavaScript as well since TypeScript is just a typed superset of JavaScript.
As per Wikipedia,
Functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
Hence in functional programming, there are two very important rules
This means:
Apart from these there are functional programming concepts below that can be applied in TypeScript, we will touch upon these further down.
Using functional programming doesn't mean its all or nothing, you can always use functional programming concepts to complement Object-oriented concepts in TypeScript. The benefits of functional programming can be utilized whenever possible regardless of the paradigm or language you use. And that is exactly what we are going to see.
TypeScript is not a purely functional language but offers a lot of concepts which are in line with functional languages, so let us see how we can apply some of the functional programming concepts above in TypeScript.
First-class functions(function as a first-class citizen) means you can assign functions to variables, pass a function as an argument to another function or return a function from another. TypeScript supports this and hence makes concepts like closures, currying, and higher-order-functions easy to write.
A function can be considered as a higher-order-function only if it takes one or more functions as parameters or if it returns another function as a result.
In TypeScript, this is quite easy to do
type mapFn = (it: string) => number;// The higher-order-function takes an array and a function as arguments
function mapForEach(arr: string[], fn: mapFn): number[] {
const newArray: number[] = [];
arr.forEach(it => {
// We are executing the method passed
newArray.push(fn(it));
});
return newArray;
}const list = [“Orange”, “Apple”, “Banana”, “Grape”];
// we are passing the array and a function as arguments to mapForEach method.
const out = mapForEach(list, (it: string): number => it.length);console.log(out); // [6, 5, 6, 5]
But then in JavaScript/TypeScript we could also simply do it this way using built-in functional methods like map, reduce and so on.
const list = [“Orange”, “Apple”, “Banana”, “Grape”];// we are passing a function as arguments to the built-in map method.
const out = list.map(it => it.length);console.log(out); // [6, 5, 6, 5]
Closures and currying are also possible in TypeScript
// this is a higher-order-function that returns a function
function add(x: number): (y: number) => number {
// A function is returned here as closure
// variable x is obtained from the outer scope of this method and memorized in the closure
return (y: number): number => x + y;
}// we are currying the add method to create more variations
var add10 = add(10);
var add20 = add(20);
var add30 = add(30);console.log(add10(5)); // 15
console.log(add20(5)); // 25
console.log(add30(5)); // 35
There are also many built-in declarative higher-order-functions in TypeScript/JavaScript like map
, reduce
, forEach
, filter
and so on. There are also many libraries that provide functional interfaces to be used in TypeScript/JavaScript.
As we saw already a pure function should return values only based on the arguments passed and should not affect or depend on global state. It is possible to do this in TypeScript easily.
This is quite simple, take the below this is a pure function. It will always return the same output for the given input and its behavior is highly predictable. We can safely cache the method if needed.
function sum(a: number, b: number): number {
return a + b;
}
If we add an extra line in this function, the behavior becomes unpredictable as it now has a side effect that affects an external state.
const holder = {};function sum(a: number, b: number): number {
let c = a + b;
holder[${a}+${b}
] = c;
return c;
}
So try to keep your functions pure and simple. Using tools like ESLint and typescript-eslint it is possible to enforce these.
Functional programming favors recursion over looping. Let us see an example for calculating the factorial of a number.
In traditional iterative approach:
function factorial(num: number): number {
let result = 1;
for (; num > 0; num–) {
result *= num;
}
return result;
}console.log(factorial(20)); // 2432902008176640000
The same can be done using recursion as below which is favored in functional programming.
const factorial = (num: number): number =>
num == 0 ? 1 : num * factorial3(num - 1);console.log(factorial(20)); // 2432902008176640000
The downside of the recursive approach is that it will be slower compared to an iterative approach most of the times(The advantage we are aiming for is code simplicity and readability) and might result in stack overflow errors since every function call needs to be saved as a frame to the stack. To avoid this tail recursion is preferred, especially when the recursion is done too many times. In tail recursion, the recursive call is the last thing executed by the function and hence the functions stack frame need not be saved by the compiler. Most compilers can optimize the tail recursion code the same way iterative code is optimized hence avoiding the performance penalty. Tail call optimization is part of the ECMAScript specs but unfortunately, most JavaScript engines do not support this yet.
Now using tail recursion the same function can be written as below, but depending on the engine this might not be optimized, though there are workarounds, still it performed better in benchmarks.
const factorialTailRec = (num: number): number => factorial(1, num);const factorial = (accumulator: number, val: number): number =>
val == 1 ? accumulator : factorial(accumulator * val, val - 1);console.log(factorialTailRec(20)); // 2432902008176640000
Consider using recursion when writing TypeScript code for readability and immutability, but if performance is critical or if the number of iterations will be huge use standard loops.
Lazy evaluation or non-strict evaluation is the process of delaying evaluation of an expression until it is needed. In general, TypeScript does strict/eager evaluation but for operands like &&
, ||
and ?:
it does a lazy evaluation. We can utilize short-circuiting, higher-order-functions, closures, and memoization techniques to do lazy evaluations.
Take this example where TypeScript eagerly evaluates everything.
function add(x: number): number {
console.log(“executing add”); // this is printed since the functions are evaluated first
return x + x;
}function multiply(x: number): number {
console.log(“executing multiply”); // this is printed since the functions are evaluated first
return x * x;
}function addOrMultiply(
add: boolean,
onAdd: number,
onMultiply: number
): number {
return add ? onAdd : onMultiply;
}console.log(addOrMultiply(true, add(4), multiply(4))); // 8
console.log(addOrMultiply(false, add(4), multiply(4))); // 16
This will produce the below output and we can see that both functions are executed always
executing add
executing multiply
8
executing add
executing multiply
16
We can use higher-order-functions to rewrite this into a lazily evaluated version
function add(x: number): number {
console.log(“executing add”);
return x + x;
}function multiply(x: number): number {
console.log(“executing multiply”);
return x * x;
}type fnType = (t: number) => number;
// This is now a higher-order-function hence evaluation of the functions are delayed in if-else
function addOrMultiply(
add: boolean,
onAdd: fnType,
onMultiply: fnType,
t: number
): number {
return add ? onAdd(t) : onMultiply(t);
}
console.log(addOrMultiply(true, add, multiply, 4));
console.log(addOrMultiply(false, add, multiply, 4));
This outputs the below and we can see that only required functions were executed
executing add
8
executing multiply
16
Or by memoization like this
const cachedAdded = {};
function add(x: number): number {
if (cachedAdded[x]) {
return cachedAdded[x];
}
console.log(“executing add”);
const out = x + x;
cachedAdded[x] = out;
return out;
}const cachedMultiplied = {};
function multiply(x: number): number {
if (cachedMultiplied[x]) {
return cachedMultiplied[x];
}
console.log(“executing multiply”);
const out = x * x;
cachedMultiplied[x] = out;
return out;
}function addOrMultiply(
add: boolean,
onAdd: number,
onMultiply: number
): number {
return add ? onAdd : onMultiply;
}console.log(addOrMultiply(true, add(4), multiply(4))); // 8
console.log(addOrMultiply(false, add(4), multiply(4))); // 16
This outputs the below and we can see that functions were executed only once for the same values
executing add
executing multiply
8
16
Please note that memoization techniques will work only when your functions are pure and referentially transparent.
There are also other ways of doing Lazy evaluations like this. Doing Lazy evaluations in TypeScript might not be worth the code complexity some of the times, but if the functions in question are heavy in terms of processing then its is absolutely worth it to lazy evaluate them.
TypeScript has a strong type system and also has great type inference. While the underlying JavaScript itself is weakly typed, TypeScript along with a compatible IDE can bridge that gap.
From Wikipedia:
Functional programs do not have assignment statements, that is, the value of a variable in a functional program never changes once defined. This eliminates any chances of side effects because any variable can be replaced with its actual value at any point of execution. So, functional programs are referentially transparent.
Unfortunately, there are not many ways to strictly limit data mutation in TypeScript, however by using pure functions and by explicitly avoiding data mutations and reassignment using other concepts we saw earlier this can be achieved. TypeScript by default passes primitive variables by value and objects by reference so we need to take care not to mutate data inside functions. Libraries like Immutable JS could also be considered. Use const
as much as possible to avoid reassignments.
For example, the below will produce an error
const list = [“Apple”, “Orange”, “Banana”, “Grape”];list = [“Earth”, “Saturn”];
But this will not help when variables are holding references to other objects, for example, the below mutation will work irrespective of the const
keyword.
const list = [“Apple”, “Orange”, “Banana”, “Grape”];list.push(“Earth”);
list.push(“Saturn”);
const
keyword allows the internal state of referenced variables to be mutated and hence from a functional programming perspective const
keyword is useful only for primitive constants and to catch reassignments.
Other techniques to follow are using built-in methods like map, reduce, filter and so on as they do not mutate the data. We can also use this ESlint plugin to restrict mutations.
When using functional programming techniques it is encouraged to use functional data types such as Stacks, Maps and Queues.
Hence maps are better than arrays or hash sets in functional programming as data stores.
This is just an introduction for those who are trying to apply some functional programming techniques in TypeScript. There are a lot more that can be done in TypeScript and with the ever-evolving ECMAScript underneath, this should be even easier. As I said earlier functional programming is not a silver bullet but it offers a lot of useful techniques for more understandable, maintainable and testable code. It can co-exist perfectly well with imperative and object-oriented programming styles. In fact, we all should be using the best of everything.
If you like this article, please leave a like or a comment and share it with all of your programming buddies!
Originally published on dev.to
#typescript #javascript #function #web-development
1596300660
For those interested in functional programming, I’ll talk about monoids and why they’re very important to understand ahead of time.
Don’t get confused: This isn’t monad — it’s monoid. I’m pretty sure you already know of monoids and you use them almost every day — you just didn’t know the term for them.
This is a series on functional programming, so you might not understand what this article is going to talk about if you haven’t read the previous posts.
Let’s assume there’s a function named identity
that takes A
and returns A
.
const identity: <A>(a: A): A => a;
interface Student {
name: string;
age: number;
}
identity<number>(3) // 3
identity<string>('hello') // hello
identity<Student>({
name: 'Bincent',
age: 5
}); // { name: 'Bincent', age: 5 }
In functional programming, this useless function (seems useless) is an important factor for many other concepts (such as monoids) that we’re about to talk about.
Basically, a monoid is a set of elements that holds the rules of the semigroup and the identity-element rule.
If S
is a set of elements, a
is a member of S
, and ·
is a proper binary operation, a·e = e·a ∈ S
must be satisfied to be a monoid.
Identity: a ∈ S, a·e = e·a = a ∈ S
Some documentation calls this using the number 1 and the any alphabet in subscript — for example, 1x referring to the identity on the variable x. Or some documentation uses just a single alphabet letter, such as i or e.
That’s all there is to know about monoids, let’s practice with some simple examples.
#typescript #programming #functional-programming #javascript #coding #function
1595593200
Functional Programming is a Declarative style of Programming Paradigm for writing computer programs.
But, What are Functions ?
Functions in general, applies computation on given input and returns the output. It relates input to an output.
f(x) = x + 2;
f(1) = 1 + 2 = 3;
f(2) = 2 + 2 = 4;
Above mentioned is a simple function that adds 2 to the input value and returns output. It relates value [1,2] => [3,4]. Similarly, a function in computer programming is a block of instruction that performs computation on given input and returns the output.
Functional Programming is such a style of writing computer programs using these tiny functions that works together and perform required computation.
Functions in Functional Programming
The philosophy of functional programming is to maintain certain characteristics while writing functions in a computer program. These characteristics are the fundamental nature of functional programming that describe what shall be the behaviour of a function. These are as follows :
Declarative
A function must be declarative, it simply tells what to compute, without specifying how to compute it.
f(x) = x * 4; 👍
Declarative function that tells to multiply input by 4;
f(x) = { y = x + x; z = x + x; return y + z;} 👎
Non-Declarative function that specify how to multiply input by 4;
Pure
A function must give the same output for a given input value, at any period of time. It is not dependent upon anything outside the function definition.
f(x) = It’s never too late; 👍
Pure function that will always return It’s never too late
f(x) = If today’s Friday or Saturday then It’s never too late else It’s late. 👎
Impure function that consider day for returning value. The value is not predictable. It can change. So a function that performs anything which is unpredictable is not a pure function. The condition or the execution is dynamic or unfixed in an impure function.
#development #functional-programming #software #software-programming #declarative-programming #function
1598093520
If you are reading this, then most probably you already know quite well what functions are in programming. A function is quite a common and spread programming construct that is present in almost all programming languages.
Generally, a function is a block of code that takes some parameters from outside, executes some operations in which these parameters may be used, then it returns an output value. Actually, in many programming languages functions are allowed to not return something or to return multiple values, not only one. But these cases can be also be represented, for the sake of generality, as only one value. For the “no return” case we can use a special value to represent that (in Python that special value is None; whenever you don’t return something from a function it’s returned None). And for the case of more values, we can use a vector of multiple values as a single object.
For example, in Python a function definition looks like this:
def func_name(param1, param2, ...):
## do some stuff with input parameters
return output_value
Now, the question I want to ask: Are these functions from programming true mathematical functions?
Well…, let’s first recall what a mathematical function is.
In mathematics, a function is just a mapping from a set A to a set B, in which any element from A has only one associated element in B.
#python #programming #function #mathematics #functional-programming
1591274700
This will be a series with a few articles in which I attempt to explain and show the use cases of some functional programming patterns.
We will work with TypeScript and use the functional programming library fp-ts. The examples and explanations are inspired by the great article series by the author of fp-ts.
#programming #typescript #javascript #functional-programming #software-development