Transparencia referencial

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 30 de noviembre de 2015; las comprobaciones requieren 9 ediciones .

La transparencia referencial y la opacidad referencial son propiedades de partes de programas de computadora . Se dice que una expresión es referencialmente transparente si puede ser reemplazada por el valor correspondiente sin cambiar el comportamiento del programa. Las funciones referencialmente transparentes se evalúan con el mismo valor para los mismos argumentos. Tales funciones se llaman funciones puras .

En matemáticas , todas las funciones son referencialmente transparentes según la definición de una función matemática . Sin embargo, en la programación no siempre es así. Para que las asociaciones semánticas adicionales de la palabra "función" no sean engañosas, a menudo se utilizan los términos " procedimiento " y " método ". En la programación funcional , solo se consideran las funciones transparentes referenciales. Algunos lenguajes de programación proporcionan un medio para garantizar la transparencia referencial. Algunos lenguajes de programación funcionales brindan transparencia referencial para todas las funciones.

La importancia de la transparencia referencial es que permite al programador y al compilador razonar sobre el comportamiento del programa como un sistema de reescritura . Puede ayudar en la validación, simplificación de algoritmos , ayuda en la modificación del código sin romperlo u optimización del código a través de la memorización , la eliminación de subexpresiones comunes , la evaluación diferida o la paralelización .

Debido a que la transparencia de enlace requiere los mismos resultados para cualquier conjunto dado de entradas en cualquier momento dado, una expresión referencialmente transparente es, por lo tanto, determinista.

Historia

Este concepto (aunque no es un término) parece haberse originado con Alfred North Whitehead y en Principios de Matemáticas de Bertrand Russell (1910-13). Willard Van Orman Quine lo adoptó en la filosofía analítica . En Word and Object (1960), Quine da esta definición:

Un modo de contención φ es referencialmente transparente si cada vez que una ocurrencia de un término singular t es puramente referencial en un término u oración ψ(t), también es puramente referencial en la palabra u oración contenedora φ(ψ(t)).

El término apareció en su uso moderno, al discutir variables en lenguajes de programación, en el conjunto original de conferencias de Christopher Strachey .« Conceptos básicos en lenguajes de programación» (1967). La bibliografía menciona Word and Object de Quine.

Ejemplos y contraejemplos

Si todas las funciones involucradas en una expresión son funciones puras, entonces la expresión es referencialmente transparente. Además, algunas funciones impuras pueden incluirse en una expresión si se descartan sus valores y sus efectos secundarios no son significativos.

Considere una función que devuelve datos de alguna fuente. En pseudocódigo, podría ser una llamada a esta función GetInput (Source), donde Sourcepodría especificar un archivo de disco, teclado, etc. específico. Incluso con los mismos valores Source, los valores de retorno sucesivos serán diferentes. Entonces la función GetInput ()no es determinista o referencialmente transparente.

Un ejemplo más sutil es una función que tiene una variable libre , es decir, depende de alguna entrada que no se pasa explícitamente como parámetro. Luego, esto se resuelve de acuerdo con las reglas para vincular un nombre a una variable no local, como una variable global , una variable en el entorno de ejecución actual (para vinculación dinámica) o una variable en un cierre (para vinculación estática). Dado que esta variable se puede cambiar sin cambiar los valores pasados ​​como parámetro, los resultados de llamadas posteriores a la función pueden diferir, incluso si los parámetros son idénticos. Sin embargo, en la programación puramente funcional, la asignación destructiva no está permitida y, por lo tanto, si una variable libre está vinculada estáticamente a un valor, la función aún tiene transparencia referencial, ya que una variable no local no puede cambiar debido a su vinculación estática.

Las operaciones aritméticas son referencialmente transparentes: por ejemplo, 5 * 5puede reemplazar con 25. De hecho, todas las funciones en el sentido matemático son referencialmente transparentes: sin (x)es transparente, ya que siempre dará el mismo resultado para cada particular x.

Las asignaciones no son transparentes. Por ejemplo, una expresión C x = x + 1 cambia el valor asignado a la variable x. Suponiendo que xinicialmente tiene un valor 10, dos evaluaciones sucesivas de la expresión dan, respectivamente 11, y 12. Obviamente, reemplazar x = x + 1con 11o 12hará que la expresión tenga diferentes valores para la misma expresión. Por lo tanto, tal expresión no es referencialmente transparente. Sin embargo, llamar a una función como int plusone (int x) {return x + 1;}es transparente porque no cambiará implícitamente el valor de entrada xy, por lo tanto, no tiene estos efectos secundarios .

La función today()no es referencialmente transparente. Si calcula esta función y la reemplaza con un valor (por ejemplo, "1 de enero de 2001"), mañana, ejecutando today(), no obtendrá el mismo resultado. Esto se debe a que el valor devuelto por la función depende del estado (fecha).

En lenguajes sin efectos secundarios, como Haskell , podemos reemplazar la igualdad con la igualdad, porque f(x) = f(x)para cualquier valor de x. Pero esto no se aplica a los idiomas con efectos secundarios.

Contraste con la programación imperativa

Si el reemplazo de una expresión por su valor es válido solo en un cierto punto del programa, entonces la expresión no es referencialmente transparente. La definición y el orden de estos puntos de secuencia es la base teórica de la programación imperativa y parte de la semántica de un lenguaje de programación imperativo.

Sin embargo, dado que una expresión referencialmente transparente puede evaluarse en cualquier momento, no es necesario especificar puntos de secuencia ni ninguna garantía de orden de evaluación. La programación sin garantías del orden de evaluación se denomina programación puramente funcional.

Uno de los beneficios de escribir código en un estilo referencialmente transparente es que hace que el compilador sea más inteligente, facilita el análisis de código estático y permite transformaciones automáticas de mejora de código . Por ejemplo, al programar en C, el rendimiento disminuirá si hay una llamada de función costosa dentro del bucle. Esto es a pesar del hecho de que la llamada a esta función se puede mover fuera del ciclo mientras los resultados del programa permanecen sin cambios. Luego, el programador necesita mover manualmente el código que contiene la llamada, posiblemente a expensas de la legibilidad. Sin embargo, si el compilador puede determinar que una función es referencialmente transparente, puede realizar esta conversión automáticamente.

La principal desventaja de los lenguajes con transparencia referencial es que hacen que las expresiones que naturalmente se ajustan a un estilo de programación secuencial imperativa sean más incómodas y menos concisas. Dichos lenguajes suelen incluir mecanismos para facilitar estas tareas conservando la cualidad puramente funcional del lenguaje, como ciertas expresiones gramaticales y mónadas .

Otro ejemplo

Usemos dos funciones como ejemplo, una es referencialmente opaca y la otra referencialmente transparente:

int valor global = 0 ; int rq ( int x ) { valor global ++ ; devuelve x + valor global ; } int rt ( int x ) { devuelve x + 1 ; }

La función rtes referencialmente transparente, lo que significa que rt(x) = rt(y)si x = y. Por ejemplo, rt(6) = 6 + 1 = 7, rt(4) = 4 + 1 = 5etc. Sin embargo, no podemos decir lo mismo de rq, porque utiliza una variable global que modifica.

La opacidad referencial rqdificulta el razonamiento sobre los programas. Por ejemplo, supongamos que queremos explicar la siguiente afirmación:

entero p = rq ( x ) + rq ( y ) * ( rq ( x ) - rq ( x ));

Podría ser tentador simplificar esta afirmación:

entero p = rq ( x ) + rq ( y ) * ( 0 ); entero p = rq ( x ) + 0 ; entero p = rq ( x );

Sin embargo, esto no funcionará para rq(), porque cada aparición rq(x)se evalúa con un valor diferente. Recuerda que el valor devuelto rqse toma de una variable global, que no se pasa y que cambia con cada llamada a rq. Esto significa que las identidades matemáticas como ya x - x = 0 {\ displaystyle x-x = 0} x-x = 0no son válidas.

Tales identidades matemáticas se mantendrán para funciones referencialmente transparentes como rt.

Sin embargo, se puede utilizar un análisis más complejo para simplificar la afirmación:

entero a = valor global ; entero p = x + a + 1 + ( y + a + 2 ) * ( x + a + 3 - ( x + a + 4 )); valor global = valor global + 4 ; entero a = valor global ; entero p = x + a + 1 + ( y + a + 2 ) * ( x + a + 3 - x - a - 4 )); valor global = valor global + 4 ; entero a = valor global ; entero p = x + a + 1 + ( y + a + 2 ) * -1 ; valor global = valor global + 4 ; entero a = valor global ; entero p = x + a + 1 - y - a - 2 ; valor global = valor global + 4 ; entero p = x - y - 1 ; valor global = valor global + 4 ;

Esto requiere más pasos y requiere cierto grado de comprensión del código que no se puede utilizar para optimizar el compilador.

Por lo tanto, la transparencia referencial nos permite pensar en nuestro código, lo que conduce a programas más confiables, la capacidad de encontrar errores que no podemos encontrar durante las pruebas y el conocimiento de las oportunidades de optimización.