Las dos condiciones que definen una función como pure son las siguientes:
Si la primera condición siempre es verdadera, ¿hay ocasiones en que la segunda condición no es verdadera?
Es decir, ¿realmente solo es necesario con la primera condición?
Aquí hay algunos contraejemplos que no cambian el alcance externo pero que aún se consideran impuros:
function a() { return Date.now(); }function b() { return window.globalMutableVar; }function c() { return document.getElementById("myInput").value; }function d() { return Math.random(); } (que ciertamente cambia el PRNG, pero no se considera observable)Acceder a variables no locales no constantes es suficiente para poder violar la segunda condición.
Siempre pienso en las dos condiciones para la pureza como complementarias:
El término efecto secundario solo se refiere al primero, la función que modifica el estado no local. Sin embargo, a veces las operaciones de lectura también se consideran efectos secundarios: cuando son operaciones e implican escritura también, incluso si su objetivo principal es acceder a un valor. Ejemplos de ello son la generación de un número pseudoaleatorio que modifica el estado interno del generador, la lectura de un flujo de entrada que avanza la posición de lectura o la lectura de un sensor externo que implica un comando de "tomar medidas".
La forma "normal" de expresar lo que es una función pura es en términos de transparencia referencial . Una función es pura si es referencialmente transparente .
Transparencia referencial significa, aproximadamente, que puede reemplazar la llamada a la función con su valor de retorno o viceversa en cualquier punto del programa, sin cambiar el significado del programa.
Entonces, por ejemplo, si el printf de C fuera referencialmente transparente, estos dos programas deberían tener el mismo significado:
printf("Hello");y
5;y todos los siguientes programas deben tener el mismo significado:
5 + 5; printf("Hello") + 5; printf("Hello") + printf("Hello"); Porque printf devuelve el número de caracteres escritos, en este caso 5.
Se vuelve aún más obvio con funciones void . Si tengo una función void foo , entonces
foo(bar, baz, quux);debería ser el mismo que
; Es decir, dado que foo no devuelve nada, debería poder reemplazarlo sin cambiar el significado del programa.
Está claro, entonces, que ni printf ni foo son referencialmente transparentes y, por lo tanto, ninguno de ellos es puro. De hecho, una función void nunca puede ser referencialmente transparente, a menos que no sea operativa.
Encuentro esta definición mucho más fácil de manejar que la que diste. También le permite aplicarlo con la granularidad que desee: puede aplicarlo a expresiones individuales, a funciones, a programas completos. Te permite, por ejemplo, hablar de una función como esta:
func fib(n): return memo[n] if memo.has_key?(n) return 1 if n <= 1 return memo[n] = fib(n-1) + fib(n-2) Podemos analizar las expresiones que componen la función y concluir fácilmente que no son referencialmente transparentes y, por lo tanto, no puras, ya que utilizan una estructura de datos mutable, a saber, la matriz memo . Sin embargo, también podemos observar la función y podemos ver que es referencialmente transparente y, por lo tanto, pura. Esto a veces se denomina pureza externa , es decir, una función que parece pura al mundo exterior, pero se implementa internamente impura.
Tales funciones siguen siendo útiles, porque mientras la impureza infecta todo a su alrededor, la interfaz externa pura crea una especie de "barrera de pureza", donde la impureza solo infecta las tres líneas de la función, pero no se filtra al resto del programa. . Estas tres líneas son mucho más fáciles de analizar para verificar su corrección que todo el programa.
Me parece que la segunda condición que ha descrito es una restricción más débil que la primera.
Déjame darte un ejemplo, supongamos que tienes una función para agregar una que también se registra en la consola:
function addOneAndLog(x) { console.log(x); return x + 1; }La segunda condición que proporcionó se cumple: esta función siempre devuelve la misma salida cuando se le da la misma entrada. Sin embargo, no es una función pura porque incluye el efecto secundario de iniciar sesión en la consola.
Una función pura es, estrictamente hablando, una función que satisface la propiedad de transparencia referencial . Esa es la propiedad de que podemos reemplazar una aplicación de función con el valor que produce sin cambiar el comportamiento del programa.
Supongamos que tenemos una función que simplemente suma:
function addOne(x) { return x + 1; } Podemos reemplazar addOne(5) con 6 en cualquier parte de nuestro programa y nada cambiará.
Por el contrario, no podemos reemplazar addOneAndLog(x) con el valor 6 en ningún lugar de nuestro programa sin cambiar el comportamiento porque la primera expresión da como resultado que se escriba algo en la consola, mientras que la segunda no.
Consideramos cualquiera de estos comportamientos adicionales que addOneAndLog(x) además de devolver la salida como un efecto secundario .