Hay mucha confusión aquí porque hay términos con varias definiciones y múltiples cosas distintas que se combinan simplemente porque generalmente se encuentran juntas.
Primero, tenemos "bloque". Eso es solo una porción léxica de código que forma una unidad, el cuerpo de un bucle, por ejemplo. Si el lenguaje realmente tiene alcance de bloque, entonces se pueden definir variables que solo existen dentro de ese bloque de código.
En segundo lugar, tenemos código invocable como tipo de valor. En los lenguajes funcionales, estos son valores de funciones, a veces llamados "funs", "funciones anónimas" (porque la función se encuentra en el valor, no el nombre asignado, no necesita un nombre para llamarlos) o " lambdas "(del operador utilizado para crearlos en el cálculo Lambda de Church). Se los puede llamar "cierres", pero no son automáticamente cierres verdaderos; para calificar, deben encapsular ("cerrar") el alcance léxico que rodea su creación, es decir, las variables definidas fuera del alcance de la función en sí, pero dentro del alcance de su definición todavía están disponibles cada vez que se llama a la función, incluso si el punto de llamada es después de que la variable referenciada saliera del alcance y se haya reciclado su almacenamiento.
Sin embargo, puede tener valores de código invocables que no son funciones completas. Smalltalk llama a estos "cierres de bloque", mientras que Ruby los llama "procs". Pero la mayoría de los Rubyistas simplemente los llaman "bloques", porque son la versión reificada de lo creado por {
... }
o do
... end
sintaxis. Lo que los diferencia de lambdas (o "cierres de funciones") es que no introducen un nuevo nivel de subrutina. Si el código en el cuerpo de un bloqueo de bloque llama a return
, se devuelve de la función/método externo el cierre de bloque existente dentro, no solo el bloque en sí.
Ese comportamiento es crítico para preservar lo que R.D. Tennent etiquetó como el "principio de correspondencia", que establece que debería poder reemplazar cualquier código con una función en línea que contenga ese código en el cuerpo y llamarlo inmediatamente. Por ejemplo, en Javascript, puede reemplazar esto:
x = 2;
con esto:
(function(){x = 2;})();
Mi ejemplo no es muy interesante, pero la capacidad de hacer este tipo de transformación sin afectar el comportamiento de la programa juega un papel clave en la refactorización funcional. El problema es que, tan pronto como haya incrustado return
declaraciones, el principio ya no se cumple.
Es por eso que Ruby tiene procs y lambdas, una fuente perenne de confusión para los novatos. Ambos procs y lambdas son objetos de la clase Proc
, pero se comportan de manera diferente, como se indicó anteriormente: un return
acaba de regresar del cuerpo de una lambda, pero regresa del método que rodea a un proceso. (Además, aunque no es relevante para la distinción que estoy dibujando aquí, lambdas comprueba arity y se queja si se llama con el número incorrecto de argumentos. Puede decir qué tipo tiene llamando al .lambda?
en el objeto.)
Javascript actualmente solo tiene cierres de función, aunque hay una propuesta sobre la mesa para introducir cierres de bloque en el idioma.
No estoy seguro de que tengan definiciones precisas, por ejemplo, los cierres que Apple agregó a gcc se llaman "bloques" – cobbal
Si estás hablando de [] 's - llegaron a Objective-C desde Smalltalk, donde están compilado en una instancia del denominado 'BlockClosure'. – daf
No está hablando de Objective-C, está hablando de las extensiones de Apple a C. En OSX 10.6 Snow Leopard, Appel presentó una nueva biblioteca de concurrencia llamada Grand Central Dispatch. Es una biblioteca basada en tareas algo similar a la Biblioteca paralela de tareas de Microsoft. La idea básica de GCD es que entregue un bloque de código a la biblioteca y luego la biblioteca decida cuándo y qué núcleo ejecutar. Normalmente, harías eso con punteros a funciones, pero Apple lo consideró demasiado feo y extendieron C con expresiones esencialmente lambda, que llaman bloques. –