En los lenguajes de programación, los cierres son una característica popular y a menudo deseada. Wikipedia dice (énfasis mío):
En informática, un cierre (...) es una función junto con un entorno de referencia para las variables no locales de esa función. Un cierre permite que una función acceda a variables fuera de su alcance léxico inmediato.
Entonces, un cierre es esencialmente un valor de función (¿anónimo?) Que puede usar variables fuera de su propio alcance. En mi experiencia, esto significa que puede acceder a las variables que están dentro del alcance en su punto de definición.
Sin embargo, en la práctica, el concepto parece ser divergente, al menos fuera de la programación funcional. Diferentes idiomas implementan diferentes semánticas, incluso parece haber guerras de opiniones sobre. Muchos programadores no parecen saber qué son los cierres, viéndolos como poco más que funciones anónimas.
Además, parece que existen obstáculos importantes al implementar cierres. Lo más notable, se suponía que Java 7 los incluiría, pero la característica se retrasó a una versión futura.
¿Por qué los cierres son tan difíciles de entender y realizar? Esta es una pregunta demasiado amplia y vaga, así que permítanme enfocarla más con estas preguntas interconectadas:
- ¿Hay problemas para expresar cierres en formalismos semánticos comunes (paso pequeño, paso grande, ...)?
- ¿Los sistemas de tipos existentes no son adecuados para cierres y no se pueden extender fácilmente?
- ¿Es problemático alinear los cierres con una traducción de procedimiento tradicional basada en pila?
Tenga en cuenta que la pregunta se refiere principalmente a lenguajes de procedimiento, orientados a objetos y scripts en general. Hasta donde yo sé, los lenguajes funcionales no tienen ningún problema.
fuente
Respuestas:
¿Puedo dirigirlo a la página de Wikipedia de problemas de Funarg ? Al menos así es como la gente del compilador solía hacer referencia al problema de implementación del cierre.
Si bien esta definición tiene sentido, no ayuda a describir el problema de implementar funciones de primera clase en un lenguaje tradicional basado en la pila de tiempo de ejecución. Cuando se trata de problemas de implementación, las funciones de primera clase se pueden dividir aproximadamente en dos clases:
El primer caso (funargs hacia abajo) no es tan difícil de implementar y se puede encontrar incluso en los lenguajes de procedimiento más antiguos, como Algol, C y Pascal. C evita el problema, ya que no permite funciones anidadas, pero Algol y Pascal hacen la contabilidad necesaria para permitir que las funciones internas hagan referencia a las variables de la pila de la función externa.
El segundo caso (funargs hacia arriba), por otro lado, requiere que los registros de activación se guarden fuera de la pila, en el montón. Esto significa que es muy fácil perder recursos de memoria a menos que el tiempo de ejecución del idioma incluya un recolector de basura. Si bien casi todo es basura recolectada hoy, requerir una sigue siendo una decisión de diseño importante y lo fue aún más hace algún tiempo.
En cuanto al ejemplo particular de Java, si recuerdo correctamente, el problema principal no era en realidad poder implementar cierres, sino cómo introducirlos al lenguaje de una manera que no fuera redundante con las características existentes (como clases internas anónimas) y eso no chocó con las características existentes (como las excepciones marcadas, un problema que no es trivial para resolver y que la mayoría de la gente no piensa al principio).
También puedo pensar en otras cosas que hacen que las funciones de primera clase sean menos triviales de implementar, como decidir qué hacer con variables "mágicas" como esta , self o super y cómo interactuar con operadores de flujo de control existentes, como break and return (¿queremos permitir devoluciones no locales o no?). Pero al final, la popularidad reciente de las funciones de primera clase parece indicar que los lenguajes que no las tienen en su mayoría lo hacen por razones históricas o debido a alguna decisión de diseño importante desde el principio.
fuente
ref
parámetro"). Si la persona que llama encapsula todas las variables de interés en la estructura, el delegado podría estar completamente estático, evitando cualquier necesidad de una asignación de montón. Los compiladores no ofrecen una buena ayuda de sintaxis para tales construcciones, pero el Framework podría admitirlos.Podemos ver cómo se implementan los cierres en C #. La escala de las transformaciones que realiza el compilador de C # deja en claro que su forma de implementar cierres es bastante trabajo. Puede haber formas más fáciles de implementar cierres, pero creo que el equipo del compilador de C # sería consciente de esto.
Considere el siguiente pseudo-C # (corté un poco de material específico de C #):
El compilador transforma esto en algo como esto:
(en realidad, la variable f todavía se creará, donde f es un 'delegado' (= puntero de función), pero este delegado todavía está asociado con el objeto theClosureObject; dejé esta parte por claridad para aquellos que no están familiarizados con C #)
Esta transformación es bastante masiva y complicada: considere los cierres dentro de los cierres y la interacción de los cierres con el resto de las características del lenguaje C #. Me imagino que la función se retrasó para Java, ya que Java 7 ya tiene muchas características nuevas.
fuente
Para responder parte de tu pregunta. El formalismo descrito por Morrisett y Harper cubre semántica de pasos grandes y pequeños de lenguajes polimórficos de orden superior que contienen cierres. Hay documentos antes de estos que proporcionan el tipo de semántica que está buscando. Mire, por ejemplo, la máquina SECD . Agregar referencias mutables o locales mutables a esta semántica es sencillo. No veo que haya problemas técnicos al proporcionar tal semántica.
fuente