Índice
- ¿Es relevante la programación funcional en la industria?
- ¿Por qué tanta gente habla de programación funcional?
- ¿Debería aprender un lenguaje funcional?
- ¿Cuáles son los desafíos?
En este artículo exploraré cómo los elementos esenciales de la programación funcional están cada vez más presentes en los lenguajes de programación imperativos, los más populares como Python, Java, Javascript, etc.
Además intentaré dar respuesta a una pregunta realmente interesante:
¿Se pueden usar los elementos de la programación funcional en lenguajes imperativos sin entender dichos conceptos dentro del paradigma funcional?
1. ¿Es relevante la programación funcional en la industria?
La programación funcional no es una idea nueva. Desde hace décadas, ha sido muy relevante en contextos académicos, especialmente en universidades. En MIT, Scheme y el clásico libro Structure and Interpretation of Computer Programs han sido la introducción fundamental a la programación durante muchos años.
A pesar del consenso académico y de que la industria está llena de casos de éxito en el uso de lenguajes funcionales (por ejemplo Whatsapp), la adopción de los mismos es mínima y se considera un nicho reservado para problemas muy específicos o para programadores e investigadores apasionados.
A tenor de los índices de popularidad de los lenguajes de programación, la respuesta es:
No, los lenguajes de programación funcional no son relevantes en la industria.
Veamos lo que dicen ciertos rankings en enero de 2025:
- Tiobe: Scala (que puede costar considerarlo como lenguaje funcional) ocupa el puesto 30, Haskell el 34 con ratings por debajo del 1% (porcentaje de programadores entrenados, cursos y consultores).
- Redmonk: ningún lenguaje funcional aparece entre los 20 primeros.
- PYPL: de nuevo Scala en el puesto 21 y Haskell en el 28 con ratings por debajo del 1% (porcentaje de búsquedas de tutoriales en Google)
- Stackoverflow Developer Survey: Scala en el puesto 26 y Elixir en el puesto 30 con ratings del 2.6% y del 2.1% (porcentaje de programadores que han hecho desarrollo extensivo en el lenguaje).
Algo que contrasta con esta aparente falta de relevancia es la alta demanda de programadores expertos en programación funcional: los tres primeros lenguajes de programación mejor pagados son lenguajes funcionales: Erlang, Elxir y Clojure (Stackoverflow Developer Survey, 2024).
2. ¿Por qué tanta gente habla de programación funcional?
Probablemente estás leyendo este post porque has oído hablar de ciertos elementos de la programación funcional que ya estás usando porque tu lenguaje favorito ya tiene o ha adoptado esos elementos. Veamos a qué elementos me estoy refiriendo y cuál es su origen.
Polimorfismo paramétrico (genéricos)
Una gran parte de los lenguajes populares han incorporado lenguajes de tipos que permiten la definición de tipos paramétricos (genéricos). Veamos un ejemplo en Java en el que se define un método de clase que invierte una lista con elementos de un tipo cualquiera:
import java.util.*;
public class ListReverser {
public static <T> List<T> reverse(List<T> list) {
List<T> reversed = new ArrayList<>();
for (int i = list.size() - 1; i >= 0; i--) {
reversed.add(list.get(i));
}
return reversed;
}
}
Los tipos paramétricos se introducen por primera vez en el lenguaje funcional ML, en el año 1975.
Orden superior
Quizás sea el elemento de la programación funcional más aceptado entre los desarrolladores de lenguajes imperativos: funciones que reciben funciones como argumentos e incluso que devuelven funciones como resultado. ¿Qué lenguaje actualmente no tiene maps, reduces, lambdas, etc.? Veamos un ejemplo de uso del orden superior para inyectar una dependencia. En este caso la dependencia es una función que sabe buscar en un repositorio, ya sea en una base de datos o, por ejemplo, a través de un API REST. En C++ esta vez:
user_t get(int id, const std::function<user_t(int)>& repository) {
try {
user_t user = repository(id);
return user;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
}
El parámetro repository será una función que sabe extraer un usuario dado su identificador, cuando se llama a get se puede pasar el repositorio que se desee.
Todos los lenguajes de programación funcional incluyen el orden superior, por datarlo: desde 1936 con la invención del cálculo lambda.
Encaje de patrones
Otro elemento que nace en el seno de un lenguaje funcional son los tipos algebraicos y el encaje de patrones (pattern matching), esta vez el lenguaje es Hope también en la década 1970s.
Cada vez más lenguajes, de forma más o menos elegante, incorporan el encaje de patrones para descomponer datos en partes como se puede ver en este código en Javascript:
function area(shape) {
const { type, ...props } = shape;
if (type === 'circle') return Math.PI * props.radius ** 2;
if (type === 'rectangle') return props.width * props.height;
throw new Error('Unknown shape');
}
Sólo los diccionarios con atributo type encajan en la asignación de shape en la línea 2. Una adopción mucho más fuerte la hace Rust, el mismo código sigue un encaje de patrones más funcional:
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn area(shape: Shape) -> f64 {
match shape {
Shape::Circle(radius) => 3.14 * radius * radius,
Shape::Rectangle(width, height) => width * height,
}
}
Inmutabilidad / transparencia referencial
El modelo de ejecución de los lenguajes imperativos se apoya intrínsecamente en la mutabilidad de un estado global. Los efectos secundarios son importantes: acoplamientos inesperados entre componentes, errores difíciles de depurar y pérdida de transparencia referencial (la misma entrada no produce la misma salida en ejecuciones diferentes). Para evitar estos problemas, cada vez son más las bibliotecas en lenguajes populares que introducen estructuras de datos inmutables. Veamos un ejemplo de uso en Python:
from immutables import Map
map = Map(a=1, b=2)
nuevo = map.set('c', 3)
print("Map:", map)
print("Nuevo:", nuevo)
Este esfuerzo por crear bibliotecas como immutables (¡mismo nombre en Java!) o incluso diseñar lenguajes como Rust para controlar el alcance de la mutabilidad es enorme. De nuevo, podría remitirme a la invención del cálculo lambda en 1936: la inmutabilidad es una característica intrínseca de los lenguajes funcionales.
3. ¿Debería aprender un lenguaje funcional?
La mayor parte de los programadores, ya sean senior o junior, han aprendido a programar con lenguajes imperativos. Todos empiezan a oír o han oído hablar de genéricos, de orden superior y funciones anónimas, de inmutabilidad e incluso de encaje de patrones. ¿Pueden aprenderse estos elementos dentro del paradigma de la programación imperativa? Mi respuesta a esta pregunta es: no.
El modelo computacional del paradigma imperativo es radicalmente diferente del paradigma funcional. El resultado es que la introducción de los elementos de la programación funcional en los lenguajes más populares no es orgánica y por eso su aprendizajes es tan difícil.
Mi conclusión es que aprender un lenguaje funcional es primordial para entender esos elementos y mi recomendación es que los equipos exploren algún lenguaje funcional.
4. ¿Cuáles son los desafíos?
Para aquellos equipos que ya están explorando algún lenguaje funcional o para aquellos que se deciden a hacerlo, desde este post, me gustaría advertir sobre los desafíos principales:
- Selección del lenguaje: Quizás sea el desafío más sencillo ya que a día de hoy existen lenguajes funcionales suficientemente maduros como para llevarlos a producción, por nombrar algunos de los más interesantes: Elixir/Erlang, Clojure, Haskell.
- Ecosistema y herramientas: La baja popularidad de dichos lenguajes hace que sea más complicado encontrar bibliotecas suficientemente maduras. Los equipos deben estar preparados para desarrollar código base o integrarse con bibliotecas implementadas en otros lenguajes.
- Disponibilidad de desarrolladores: Encontrar desarrolladores familiarizados con el paradigma funcional es un reto. Los equipos deben estar preparados para formar desarrolladores.
- Curva de aprendizaje: Introducir principios funcionales en equipos con experiencia predominantemente imperativa llevará tiempo y debe hacerse al margen de ese conocimiento previo de los programadores.