Lo he visto en paradigmas imperativos
f (x) + f (x)
podría no ser lo mismo que:
2 * f (x)
Pero en un paradigma funcional debería ser lo mismo. He tratado de implementar ambos casos en Python y Scheme , pero para mí se ven bastante directos.
¿Cuál sería un ejemplo que pudiera señalar la diferencia con la función dada?

f(x++)+f(x++)podría no ser la misma que2*f(x++)(? en C es especialmente bonito cuando cosas como que se oculta dentro de macros - hizo que me rompí la nariz en que se apuesta)f(x++)+f(x++)puede ser absolutamente cualquier cosa, ya que invoca un comportamiento indefinido. Pero eso no está realmente relacionado con la transparencia referencial, lo que no ayudaría para esta convocatoria, está 'indefinido' para funciones referencialmente transparentes comosin(x++)+sin(x++)también en. Podría ser 42, podría formatear su disco duro, podría tener demonios volando de la nariz del usuario ...Respuestas:
La transparencia referencial, referida a una función, indica que puede determinar el resultado de aplicar esa función solo observando los valores de sus argumentos. Puede escribir funciones referencialmente transparentes en cualquier lenguaje de programación, por ejemplo, Python, Scheme, Pascal, C.
Por otro lado, en la mayoría de los idiomas también puede escribir funciones transparentes no referenciales. Por ejemplo, esta función de Python:
no es referencialmente transparente, de hecho llama
y
producirá diferentes valores, para cualquier argumento
x. La razón de esto es que la función usa y modifica una variable global, por lo tanto, el resultado de cada invocación depende de este estado cambiante, y no solo del argumento de la función.Haskell, un lenguaje puramente funcional, separa estrictamente la evaluación de expresiones en la que se aplican funciones puras y que siempre es referencialmente transparente, de la ejecución de la acción (procesamiento de valores especiales), que no es referencialmente transparente, es decir, ejecutar la misma acción puede tener cada vez que Resultado diferente.
Entonces, para cualquier función de Haskell
y cualquier número entero
x, siempre es cierto queUn ejemplo de una acción es el resultado de la función de biblioteca
getLine:Como resultado de la evaluación de expresiones, esta función (en realidad una constante) produce en primer lugar un valor puro de tipo
IO String. Los valores de este tipo son valores como cualquier otro: puede pasarlos, colocarlos en estructuras de datos, componerlos usando funciones especiales, etc. Por ejemplo, puede hacer una lista de acciones de esta manera:Las acciones son especiales, ya que puede indicarle al tiempo de ejecución de Haskell que las ejecute escribiendo:
En este caso, cuando se inicia su programa Haskell, el tiempo de ejecución recorre la acción vinculada
mainy la ejecuta , posiblemente produciendo efectos secundarios. Por lo tanto, la ejecución de la acción no es referencialmente transparente porque ejecutar la misma acción dos veces puede producir resultados diferentes dependiendo de lo que el tiempo de ejecución obtenga como entrada.Gracias al sistema de tipos de Haskell, una acción nunca se puede usar en un contexto donde se espera otro tipo, y viceversa. Entonces, si desea encontrar la longitud de una cadena, puede usar la
lengthfunción:devolverá 5. Pero si desea encontrar la longitud de una cadena leída desde el terminal, no puede escribir
porque obtiene un error de tipo:
lengthespera una entrada de la lista de tipos (y una Cadena es, de hecho, una lista) perogetLinees un valor de tipoIO String(una acción). De esta manera, el sistema de tipos asegura que un valor de acción comogetLine(cuya ejecución se lleva a cabo fuera del lenguaje central y que puede ser transparente no referencial) no puede ocultarse dentro de un valor de tipo de no acciónInt.EDITAR
Para responder a una pregunta exizt, aquí hay un pequeño programa Haskell que lee una línea desde la consola e imprime su longitud.
La acción principal consta de dos subacciones que se ejecutan secuencialmente:
getlinede tipoIO String,putStrLnde tipoString -> IO ()en su argumento.Más precisamente, la segunda acción es construida por
lineal valor leído por la primera acción,length(calcular la longitud como un entero) y luegoshow(convertir el entero en una cadena),putStrLnal resultado deshow.En este punto, se puede ejecutar la segunda acción. Si ha escrito "Hola", imprimirá "5".
Tenga en cuenta que si obtiene un valor de una acción usando la
<-notación, solo puede usar ese valor dentro de otra acción, por ejemplo, no puede escribir:porque
show (length line)tiene tipoStringmientras que la notación do requiere que una acción (getLinede tipoIO String) sea seguida por otra acción (por ejemplo,putStrLn (show (length line))de tipoIO ()).EDITAR 2
La definición de Jörg W Mittag de transparencia referencial es más general que la mía (he votado su respuesta). He usado una definición restringida porque el ejemplo en la pregunta se enfoca en el valor de retorno de las funciones y quería ilustrar este aspecto. Sin embargo, RT en general se refiere al significado de todo el programa, incluidos los cambios en el estado global y las interacciones con el entorno (IO) causadas por la evaluación de una expresión. Entonces, para una definición correcta y general, debe referirse a esa respuesta.
fuente
IOtipo de Haskell con bastante facilidad en cualquier idioma con lambdas y genéricos, pero como cualquiera puede llamarprintlndirectamente, la implementaciónIOno garantiza la pureza; sería simplemente una convención.getLineno ser referencialmente transparente es incorrecto. Está presentandogetLinecomo si se evalúa o se reduce a una Cadena, cuya Cadena particular depende de la entrada del usuario. Esto es incorrecto.IO Stringno contiene una cadena más deMaybe Stringlo que lo hace.IO Stringes una receta para tal vez, posiblemente obtener una Cadena y, como expresión, tan pura como cualquier otra en Haskell.Sin embargo, eso no es lo que significa Transparencia referencial. RT significa que puede reemplazar cualquier expresión en el programa con el resultado de evaluar esa expresión (o viceversa) sin cambiar el significado del programa.
Tomemos, por ejemplo, el siguiente programa:
Este programa es referencialmente transparente. Puedo reemplazar una o ambas ocurrencias de
f()con2y todavía funcionará igual:o
o
Todos se comportarán igual.
Bueno, en realidad, hice trampa. Debería poder reemplazar la llamada a
printcon su valor de retorno (que no tiene ningún valor) sin cambiar el significado del programa. Sin embargo, claramente, si solo elimino las dosprintdeclaraciones, el significado del programa cambiará: antes, imprimió algo en la pantalla, después no lo hace. I / O no es referencialmente transparente.La regla general es: si puede reemplazar cualquier expresión, subexpresión o llamada de subrutina con el valor de retorno de esa expresión, subexpresión o llamada de subrutina en cualquier parte del programa, sin que el programa cambie su significado, entonces tiene referencial transparencia. Y lo que esto significa, prácticamente hablando, es que no puede tener ninguna E / S, no puede tener ningún estado mutable, no puede tener ningún efecto secundario. En cada expresión, el valor de la expresión debe depender únicamente de los valores de las partes constituyentes de la expresión. Y en cada llamada de subrutina, el valor de retorno debe depender únicamente de los argumentos.
fuente
printejemplo. Quizás una forma de ver esto, es que lo que está impreso en la pantalla es parte del "valor de retorno". Si puede reemplazarprintcon su función el valor de retorno y la escritura equivalente en el terminal, el ejemplo funciona.4y2 + 2no intercambiable, ya que tienen diferentes tiempos de funcionamiento, y el punto central de la transparencia referencial es que se puede sustituir una expresión con lo que se evalúa como. La consideración importante sería la seguridad del hilo.listOfSequence.append(n)rendimientosNone, por lo que debe ser capaz de reemplazar a cada llamadalistOfSequence.append(n)conNonesin cambiar el sentido de su programa. ¿Puedes hacer eso? Si no, entonces no es referencialmente transparente.Parte de esta respuesta se toma directamente de un tutorial inacabado sobre programación funcional , alojado en mi cuenta de GitHub:
Considere un ejemplo simple:
En un lenguaje funcional puro, el lado izquierdo y derecho del signo igual son sustituibles entre sí en ambos sentidos. Es decir, a diferencia de un lenguaje como C, la notación anterior realmente afirma una igualdad. Una consecuencia de esto es que podemos razonar sobre el código del programa al igual que las ecuaciones matemáticas.
De la wiki de Haskell :
Para contrastar esto, el tipo de operación realizada por lenguajes tipo C a veces se denomina asignación destructiva .
El término puro se usa a menudo para describir una propiedad de expresiones, relevante para esta discusión. Para que una función se considere pura,
De acuerdo con la metáfora de la caja negra, que se encuentra en numerosos libros de texto matemáticos, los elementos internos de una función están completamente sellados del mundo exterior. Un efecto secundario es cuando una función o expresión viola este principio, es decir, el procedimiento puede comunicarse de alguna manera con otras unidades del programa (por ejemplo, compartir e intercambiar información).
En resumen, la transparencia referencial es imprescindible para que las funciones se comporten como verdaderas funciones matemáticas también en la semántica de los lenguajes de programación.
fuente
[here](link to source)..." seguido de 2) el formato correcto de las comillas (use comillas, o mejor aún,>símbolo para eso). Tampoco pasaría nada si además de dar una orientación general, las direcciones de responder a la pregunta concreta preguntó acerca de, en este caso sobref(x)+f(x)/2*f(x), ver Cómo Answer - de lo contrario puede parecer que es simplemente la publicidad de su página