TL; DR Como otros señalaron: la notación lambda es solo una forma de definir funciones sin verse obligado a darles un nombre.
Versión larga
Me gustaría elaborar un poco sobre este tema porque me parece muy interesante. Descargo de responsabilidad: he tomado mi curso sobre cálculo lambda hace mucho tiempo. Si alguien con mejor conocimiento encuentra alguna imprecisión en mi respuesta, siéntase libre de ayudarme a mejorarla.
Comencemos con expresiones, por ejemplo, 1 + 2
y x + 2
. Los literales como 1
y 2
se llaman constantes porque están vinculados a valores fijos específicos.
Un identificador como x
se llama variable y para evaluarlo primero debe vincularlo a algún valor. Entonces, básicamente no puedes evaluar x + 1
mientras no sepas qué x
es.
La notación lambda proporciona un esquema para vincular valores de entrada específicos a variables. Se puede formar una expresión lambda agregando λx .
delante de una expresión existente, por ejemplo λx . x + 1
. Variable x
se dice que es libre en x + 1
y con destino enλx . x + 1
¿Cómo ayuda esto a evaluar expresiones? Si alimenta un valor a la expresión lambda, así
(λx . x + 1) 2
entonces puede evaluar la expresión completa reemplazando (vinculando) todas las apariciones de la variable x
con el valor 2:
(λx . x + 1) 2
2 + 1
3
Entonces, la notación lambda proporciona un mecanismo general para vincular cosas a variables que aparecen en un bloque de expresión / programa. Dependiendo del contexto, esto crea conceptos visualmente diferentes en los lenguajes de programación:
- En un lenguaje puramente funcional como Haskell, las expresiones lambda representan funciones en el sentido matemático: se inyecta un valor de entrada en el cuerpo de la lambda y se produce un valor de salida.
- En muchos lenguajes (por ejemplo, JavaScript, Python, Scheme), evaluar el cuerpo de una expresión lambda puede tener efectos secundarios. En este caso, se puede usar el término procedimiento para marcar la diferencia con funciones puras puras.
Además de las diferencias, la notación lambda se trata de definir parámetros formales y vincularlos a parámetros reales.
El siguiente paso es darle un nombre a una función / procedimiento. En varios idiomas, las funciones son valores como cualquier otro, por lo que puede asignar un nombre a una función de la siguiente manera:
(define f (lambda (x) (+ x 1))) ;; Scheme
f = \x -> x + 1 -- Haskell
val f: (Int => Int) = x => x + 1 // Scala
var f = function(x) { return x + 1 } // JavaScript
f = lambda x: x + 1 # Python
Como señaló Eli Barzilay, esta definición solo une el nombre f
a un valor, que resulta ser una función. Entonces, a este respecto, las funciones, números, cadenas, caracteres son todos valores que pueden vincularse a los nombres de la misma manera:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
En estos idiomas también puede vincular una función a un nombre utilizando la notación más familiar (pero equivalente):
(define (f x) (+ x 1)) ;; Scheme
f x = x + 1 -- Haskell
def f(x: Int): Int = x + 1 // Scala
function f(x) { return x + 1 } // JavaScript
def f(x): return x + 1 # Python
Algunos lenguajes, por ejemplo, C, solo admiten la última notación para definir funciones (con nombre).
Cierres
Algunas observaciones finales sobre cierres . Considera la expresión x + y
. Este contiene dos variables libres. Si te unes x
usando la notación lambda obtienes:
\x -> x + y
Esto no es (todavía) una función porque todavía contiene una variable libre y
. También puede hacer una función al vincularlo y
:
\x -> \y -> x + y
o
\x y -> x + y
que es lo mismo que la +
función.
Pero puede vincular, por ejemplo, y
de otra manera (*):
incrementBy y = \x -> x + y
El resultado de aplicar la función incrementBy a un número es un cierre, es decir, una función / procedimiento cuyo cuerpo contiene una variable libre (por ejemplo y
) que se ha vinculado a un valor del entorno en el que se definió el cierre.
Así incrementBy 5
es la función (cierre) que incrementa los números en 5.
NOTA (*)
Estoy engañando un poco aquí:
incrementBy y = \x -> x + y
es equivalente a
incrementBy = \y -> \x -> x + y
entonces el mecanismo de enlace es el mismo. Intuitivamente, pienso en un cierre como la representación de un trozo de una expresión lambda más compleja. Cuando se crea esta representación, algunos de los enlaces de la expresión madre ya se han establecido y el cierre los usa más tarde cuando se evalúa / invoca.