Calcular si una función es pura

11

Según Wikipedia:

En la programación de computadoras, una función puede describirse como pura si ambas afirmaciones sobre la función se mantienen: la función siempre evalúa el mismo valor de resultado dados los mismos valores de argumento. El valor del resultado de la función no puede depender de ninguna información oculta o estado que pueda cambiar a medida que avanza la ejecución del programa o entre diferentes ejecuciones del programa, ni puede depender de ninguna entrada externa de los dispositivos de E / S. La evaluación del resultado no causa ningún efecto secundario o salida semánticamente observable, como la mutación de objetos mutables o la salida a dispositivos de E / S.

Me pregunto si es posible escribir una función que calcule si una función es pura o no. Código de ejemplo en Javascript:

function sum(a,b) {
    return a+b;
}

function say(x){
    console.log(x);
}

isPure(sum) // True
isPure(say) // False
Oni
fuente
77
Este podría ser candidato para el intercambio de pila de informática . Esto tiende más al ámbito de la teoría ("posible") que a la práctica y el diseño ... Está un poco más allá de mi comprensión.
... y aunque es "posible" y "teoría", es muy probable que haya una respuesta real (una buena opción para preguntas y respuestas) que incluso puede implicar una prueba ... que nuevamente lo lleva de vuelta al mundo de la informática .
Creo que puede determinar si una función es pura mirando el tipo de funciones que se invocan en su definición. Quiero decir, puedes definir la pureza por inducción en la estructura de funciones.
Giorgio el
77
No es posible prescindir de hacks de reflexión específicos de la plataforma. Si una función es una caja negra, ningún experimento puede probar que es pura. Por ejemplo, si hay algo así if (rand(1000000)<2) return WRONG_ANSWER, probar la función muchas veces para obtener un comportamiento consistente no ayudará. Pero, si tiene acceso a la definición de la función, la prueba es trivial.
SK-logic
1
@ user470365 De la definición de una función pura : "La evaluación del resultado no causa ningún efecto secundario o salida semánticamente observable, como la mutación de objetos mutables o la salida a dispositivos de E / S". - Cualquier cosa que escriba a IO es impura por definición. En el ejemplo, las sayllamadas console.logque son impuras también sayson impuras.

Respuestas:

17

Sí, es posible, dependiendo del idioma.

En JavaScript, puede saber si una función es pura con los siguientes criterios:

  • Solo lee parámetros y locales;

  • Solo escribe locales;

  • En no locales, solo llama a funciones puras;

  • Todas las funciones que llama implícitamente son puras, por ejemplo toString; y

  • Solo escribe propiedades de locales si no alias no locales.

El aliasing no es posible determinar en JavaScript en el caso general, porque siempre puede buscar propiedades de un objeto dinámicamente ( object["property"]). Siempre que nunca haga eso, y tenga la fuente de todo el programa, entonces creo que el problema es manejable. También necesitaría información sobre qué funciones nativas tienen efectos secundarios, como console.logo casi cualquier cosa que involucre al DOM.

El término "puro" también podría usar alguna aclaración. Incluso en un lenguaje de programación puramente funcional, fuertemente tipado y estático, donde todas las funciones son referencialmente transparentes, una función puede fallar al finalizar. Entonces, cuando hablamos id :: a -> a, lo que realmente estamos diciendo no es:

Dado algún valor de tipo a, la función idproduce un valor de tipo a.

Pero más bien:

Teniendo en cuenta un valor de tipo a, la función idno no producir un valor que es no de tipo a.

Porque una implementación válida de ides error "Not implemented!". Como Peteris señala, esta no totalidad podría verse como una especie de impureza. Koka es un lenguaje de programación funcional, con una sintaxis modelada en JavaScript, que puede inferir posibles efectos como divergencia (no terminación), transparencia referencial, lanzamiento de excepciones y acciones de E / S.

Jon Purdy
fuente
+1 - Dada la respuesta de Peteris obtuvo tantos votos positivos ...
mattnz
Supongo que debería agregar "Solo llama a funciones puras" a la lista de criterios.
Greg Hewgill
1
@GregHewgill: Buena captura. Actualicé la respuesta en consecuencia. Está bien llamar a las funciones de mutación en locales, siempre que no sean de otro modo colaterales. "Puro" es un término demasiado sobrecargado ...
Jon Purdy
También debe verificar si toStringes puro para cualquier objeto que use como cadena.
Oleg V. Volkov
No creo que esta respuesta sea correcta, porque solo hay un subconjunto de circunstancias en las que puedes hacer estas determinaciones. Considere: ¿es pura esta función? function a (o) { return o.method(); }- en realidad no podemos responder esto, porque depende de qué parámetro ose pase. Además, no podemos dar cuenta de lo que sucede si una función pura previamente certificada se cambia a una implementación no pura, que siempre es un problema potencial con JavaScript.
Julio
11

No. Puede verificar fácilmente si una función solo realiza operaciones "puramente seguras", como se describe en la respuesta de Jon Purdy, pero eso es IMO que no es suficiente para responder la pregunta.

Considere esta función:

function possiblyPure(x) {
    if (someCheck(x)) {
        return x+1; // pure code path
    }
    else {
        console.log("I'm so unpure..."); // unpure code path
    }
}

Obviamente, si someCheckes impuro, también lo es possiblyPure. Pero, si someCheckes puro y devuelve truetodos los valores posibles de x, possiblyPurees puro, ¡ya que la ruta del código no es inalcanzable!

Y aquí viene la parte difícil: determinar si someCheckdevuelve verdadero o no para cada entrada posible. Intentar responder a esa pregunta inmediatamente te lleva al reino del problema de detención y problemas similares indecidibles.

EDITAR: prueba de que es imposible

Existe cierta incertidumbre sobre si una función pura debe terminar en cada entrada posible o no. Pero en ambos casos, el problema de detención puede usarse para mostrar que la verificación de pureza es imposible.

Caso A) Si se requiere una pura función de interrumpir en cada entrada posible, se tiene que resolver el problema de la parada para determinar si es o no la función es puro. Como se sabe que esto es imposible, según esta definición, la pureza no puede calcularse.

Caso B) Si se permite que una función pura no termine en algunas entradas, podemos construir algo así: supongamos que isPure(f)calcula si fes una cadena que define una función pura.

function halts(f) {
   var fescaped = f.replace(/\"/g, '\\"');
   var upf = 'function() { '+f+'("'+fescaped+'\); console.log("unpure"); }';
   return isPure(upf);
}

Ahora isPuretiene que determinar si se fdetiene o no en su propia fuente como entrada. Si se detiene, upfes impuro; si no termina, upfes puro iff fes puro.

Si isPurefuncionó como se esperaba (devuelve resultados correctos y termina en cada entrada), ¡habríamos resuelto el problema de detención (*)! Como se sabe que esto es imposible, isPureno puede existir.

(*) para funciones de JavaScript puro, que es suficiente para resolverlo también en la máquina de turing.

usuario281377
fuente
3
Cierto. Siempre es posible hacer un análisis conservador : verificar si una función es definitivamente pura, pero no es posible verificar si definitivamente no es pura.
SK-logic
Muchos casos son trivialmente decidibles: esas funciones puras descritas por Jon Purdy o funciones impuras que incondicionalmente hacen algo sucio; pero en general, uno puede construir casos que son indecidibles.
usuario281377
1

Esta pregunta de stackoverflow tiene una respuesta de yfeldblum que es relevante aquí. (Y tiene un voto negativo por alguna razón que no puedo entender. ¿Sería una mala etiqueta votar a favor de algo que tiene 3 años?) Él da una prueba de que si una función es pura es reducible al problema de detención en un comentario.

Creo que desde un punto de vista práctico no sería demasiado difícil para algunos idiomas si dejas que la función devuelva sí, no o tal vez. Estaba viendo un video sobre Clojure hace un par de días, y el orador había hecho un recuento de casos de impureza en una base de código buscando alrededor de 4 cadenas diferentes (como "ref"). Debido al énfasis de Clojure en la pureza y la segregación de las cosas impuras, fue trivial, pero no era exactamente lo que estaba buscando.

Entonces, teóricamente imposible, prácticamente posible si modifica un poco la pregunta, y creo que lo difícil que sería dependería en gran medida del idioma. Lenguajes más simples / limpios con un enfoque en la inmutabilidad y una buena reflexión sería más fácil.

Michael Shaw
fuente
Creo que fue rechazado porque está mal. bottomes un valor válido de primera clase, no merece ser discriminado de esta manera.
SK-logic
0

Gran pregunta

Lo mejor que puede hacer en la práctica, suponiendo que no tenga la capacidad de escuchar acciones de E / S para llamar a la función tantas veces como sea posible. Luego vea si el valor de retorno es consistente.

Pero no puedes hacer esto en general. Podría decirse que los programas que no se detienen no son puros, y no podemos decidir el problema de detención.

Pedro es
fuente
1
-1: Sería más que trivial escribir una función que pase esta prueba y sea cualquier cosa menos pura.
mattnz
3
Según esta lógica, cualquier voidfunción sería "pura", lo cual es claramente falso.
Greg Hewgill
1
@ Greg: Por extensión, void foo (void) también tendría que ser puro.
mattnz
0

No es posible en el caso general. Ver problema de detención . Brevemente, es imposible escribir un programa que, dada una función arbitraria y una entrada, determina si el programa se detendrá o se ejecutará para siempre. Si se ejecuta para siempre, no es una función pura que se ajuste a la definición que dio.

dbkk
fuente
55
Correr para siempre no parece descartar que una función se ajuste a sus criterios para una función pura.
cuál es el
+1: Se requiere implícitamente que la función finalice con "La función siempre evalúa el mismo valor de resultado que da ..."
mattnz
2
Ejecutar constantemente para siempre sin modificar ningún estado es perfectamente "puro". Pero, por supuesto, es un problema de terminología aquí.
SK-logic
@mattnz, dicha función siempre evaluará el bottomvalor.
SK-logic
1
Puedo ver dónde entra el problema de la terminología. En algunas interpretaciones, una función "pura" es una que es determinista y que nunca comunica ningún estado o valor con el exterior durante su ejecución. En otras interpretaciones, la detención se agrega a los requisitos. Con la primera interpretación, es fácil determinar si una función es pura: una máquina que ejecuta un lenguaje en particular debería poder determinar si un programa en ese idioma se comunica con el exterior.
rwong