Skip to main content

Command Palette

Search for a command to run...

What is functional programming using functors, monads and promises?

Published
4 min readView as Markdown

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:

  1. 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.

  1. 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}");
            }
        }
  1. 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}");
        }
    }
}

More from this blog

Functional Programming

13 posts