What is functional programming using functors, monads and promises?
Functional programming is a paradigm that emphasizes the use of pure functions and immutable data to write robust and scalable code. It provides a set of powerful tools and concepts to tackle complex problems efficiently. In this article, we explore three fundamental concepts of functional programming: functors, monads, and promises. We will see what they are, how they work, and provide code examples to illustrate their use.
Functores:
Functors are a key concept in functional programming that provide a way to apply a function to a value within a context. They allow us to work with values that can be mapped onto, providing a consistent interface to transform these values.
Monads:
Monads are another important concept in functional programming that provides a way to chain operations on values within a context. They encapsulate a value and provide a set of operations to transform and combine these values while maintaining the context.
Promesas:
Promises are a widely used concept in JavaScript to handle asynchronous operations. They represent a value that may be available in the future and provide a set of methods for handling success and error.
Example:
Functores:
- In this code, the asynchronous functions DoubleAsync, AddOneAsync and DivideByTwoAsync act as functors. These functions take a value as input (the number) and apply a transformation to that value transparently.
static async Task<double> DoubleAsync(int number)
{
await Task.Delay(1000);
return number * 2;
}
static async Task<double> AddOneAsync(double number)
{
await Task.Delay(10);
return number + 1;
}
static async Task<double> DivideByTwoAsync(double number)
{
await Task.Delay(1000);
if (number == 0)
{
throw new DivideByZeroException("No se puede dividir por cero.");
}
return number / 2;
}
For example, DoubleAsync doubles the number, AddOneAsync adds one to the number and DivideByTwoAsync divides the number by two.
These functions are similar to functors because they apply an operation to a value encapsulated within an asynchronous task.
Monads:
In the Main method, the await operator is used to chain asynchronous operations in a sequential and orderly manner.
Each call to an asynchronous function waits for the previous operation to complete before executing, ensuring a predictable and orderly flow of control.
This chaining of asynchronous operations using await is similar to the use of monads in functional programming to chain operations in a controlled and orderly manner.
static async Task Main(string[] args)
{
try
{
int number = 5;
double doubledResult = await DoubleAsync(number);
double addedOneResult = await AddOneAsync(doubledResult);
double finalResult = await DivideByTwoAsync(addedOneResult);
Console.WriteLine($"El resultado final es: {finalResult}"); // Output: El resultado final es: 5.5
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
Promesas:
In C#, asynchronous operations are represented by Task objects, which are similar to promises in JavaScript.
Each asynchronous function returns a Task object, which represents the eventual result of the asynchronous operation.
For example, DoubleAsync, AddOneAsync and DivideByTwoAsync return tasks that represent the eventual result of doubling, adding one and dividing the number respectively.
Using these tasks in C# allows you to handle asynchronous operations efficiently and elegantly, similar to handling promises in JavaScript.
static async Task<double> DoubleAsync(int number)
Complete Code:
using System;
using System.Threading.Tasks;
class Program
{
// Definimos una función asincrónica que duplica un número
static async Task<double> DoubleAsync(int number)
{
// Simulamos una operación asincrónica de espera
await Task.Delay(1000);
// Devolvemos el número duplicado
return number * 2;
}
// Definimos una función asincrónica que suma uno al número
static async Task<double> AddOneAsync(double number)
{
// Simulamos una operación asincrónica de espera
await Task.Delay(10);
// Devolvemos el número incrementado en uno
return number + 1;
}
// Definimos una función asincrónica que divide el número por dos
static async Task<double> DivideByTwoAsync(double number)
{
// Simulamos una operación asincrónica de espera
await Task.Delay(1000);
// Verificamos si el número es cero antes de dividir
if (number == 0)
{
throw new DivideByZeroException("No se puede dividir por cero.");
}
// Devolvemos el resultado de la división
return number / 2;
}
static async Task Main(string[] args)
{
try
{
// Definimos un número inicial
int number = 5;
// Encadenamos las operaciones para obtener un resultado final
double doubledResult = await DoubleAsync(number);
double addedOneResult = await AddOneAsync(doubledResult);
double finalResult = await DivideByTwoAsync(addedOneResult);
// Imprimimos el resultado final en la consola
Console.WriteLine($"El resultado final es: {finalResult}"); // Output: El resultado final es: 5.5
}
catch (Exception ex)
{
// Capturamos y manejamos cualquier excepción que ocurra
Console.WriteLine($"Error: {ex.Message}");
}
}
}