Closures en F#

En mi anterior post sobre F#:

Primeros pasos en F#

había mencionado que no hay variables en el lenguaje, que hay identificadores. ¿cómo es eso? Exploremos el concepto de closure (podría traducirlo cierre, cerramiento, clausura), y cómo se usa en F# (closure es una característica de varios lenguajes).

De acuerdo al artículo de la Wikipedia sobre closures en ciencias de la computación:

a closure is a function that is evaluated in an environment containing one or more bound variables
(una closure es una función que es evaluada en un ambiente que contiene uno o más variables ligadas)

Bueno, ¿qué significa esto? Para entenderlo, asignemos un valor a un identificador, usando el fsi.exe, el intérprete interactivo de F# (podríamos usar Visual Studio también):

MSR F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.3.4, compiling for .NET Framework Version v2.0.50727

NOTE:
NOTE: See ‘fsi –help’ for flags
NOTE:
NOTE: Commands: #r <string>; reference (dynamically load) the given DLL.
NOTE: #I <string>; add the given search path for referenced DLLs.

NOTE: #use <string>; accept input from the given file.
NOTE: #load <string> …<string>;
NOTE: load the given file(s) as a compilation unit.
NOTE: #time;; toggle timing on/off.
NOTE: #types;; toggle display of types on/off.
NOTE: #quit;; exit.
NOTE:
NOTE: Visit the F# website at
http://research.microsoft.com/fsharp.
NOTE: Bug reports to fsbugs@microsoft.com. Enjoy!

> let x = 2;;

val x : int

Ahora, tenemos un identificador x. Podemos mostrar su valor con:

> x;;
val it : int = 2

Un simple y conocido entero. Ahora, no podemos cambiar su valor: este identificador fue escrito en piedra como que es el entero 2. Nada en la historia humana y de F# podría cambiar sy valor. ¿Será así? Definamos una función:

> let dup n = n * x;;

val dup : int -> int

Esta función dup toma un argumento n y lo multiplica por el valor del identificador x. Este identificador es una “variable” libre en la función: no es un identificador local, viene de afuera de ella. Durante la evaluación de la función, x toma su valor del “environment”, del ambiente en el que estaba durante la definición de dup. Probemos ahora:

> dup 3;;
val it : int = 6

El resultado fue seis, como esperábamos. Pero ahora, veamos de “cambiar” el valor de x:

> let x = 3;;

val x : int

> x;;
val it : int = 3

En este punto, tenemos que comenzar a hablar de closures: este “x” es OTRO identificador, que oculta el anterior. PERO, para la función dup, el identificador x es TODAVIA el anterior. Veamos de comprobar esto, invocando de nuevo la función:

> dup 3;;
val it : int = 6

¡¡El dup todavía usa el x original!! Aquí está la closure en acción. Si intentamos:

> dup;;
val it : (int -> int) = <fun:clo@0>

Notemos el clo @ 0 : es la firma de la closure que esta función está usando. Apunta al ambiente original donde la función había sido definida.

Esta característica implica que la conducta de una función no cambia luego de su definición. Aún las aparentemente “variables libres” están ligadas en tiempo de definición. Toto esto es parte de los fundamentos sólidos de la programación funcional, en general, y de F#, en particular.

Sobre closures en general y conceptos relacionados, como los lambdas, tenemos para leer a:

The Original ‘Lambda Papers’ by Guy Steele and Gerald Sussman

Podemos aprender mucho de Lisp, lambdas y closures, viendo:

Closures + Lambda = The key to OOP in Lisp « Learning Lisp

Implementé algunas lambdas y closures, en mi intérprete Lisp:

AjLisp: a Lisp interpreter in .NET

Más sobre closures y C# en:

What’s In A Closure?
Fibonacci Numbers, Caching and Closures

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com

This entry was posted in 1389, 6269, 8848, 8871. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>