react pure programming languages functional example books functional-programming side-effects

functional-programming - pure - functional programming vs oop



¿Por qué el aumento de una excepción es un efecto secundario? (4)

De acuerdo con la entrada de wikipedia para efectos secundarios , presentar una excepción constituye un efecto secundario. Considera esta simple función de python:

def foo(arg): if not arg: raise ValueError(''arg cannot be None'') else: return 10

Invocarlo con foo(None) siempre se cumplirá con una excepción. La misma entrada, la misma salida. Es referencialmente transparente. ¿Por qué esto no es una función pura?


Desde la primera línea:

"En informática, se dice que una función o expresión tiene un efecto secundario si, además de devolver un valor, también modifica algún estado o tiene una interacción observable con las funciones de llamada o el mundo exterior".

El estado que modifica es la terminación del programa. Para responder a su otra pregunta sobre por qué no es una función pura. La función no es pura porque lanzar una excepción termina el programa, por lo tanto, tiene un efecto secundario (el programa termina).


La generación de una excepción puede ser pura O no pura, solo depende del tipo de excepción que se genera. Una buena regla empírica es si la excepción es planteada por código, es pura, pero si el hardware la plantea, generalmente debe clasificarse como no pura.

Esto se puede ver al observar lo que ocurre cuando el hardware genera una excepción: primero se genera una señal de interrupción, luego el manejador de interrupción comienza a ejecutarse. El problema aquí es que el manejador de interrupciones no era un argumento para su función ni especificado en su función, sino una variable global. Cada vez que se lee o escribe una variable global (también conocida como estado), ya no tiene una función pura.

Compare esto con una excepción que se plantea en su código: usted construye el valor de la Excepción a partir de un conjunto de argumentos o constantes conocidas de ámbito local, y "arroja" el resultado. No hay variables globales utilizadas. El proceso de arrojar una excepción es esencialmente azúcar sintáctica proporcionada por su idioma, no introduce ningún comportamiento no determinista o no puro. Como dijo Don, "debe ser semánticamente equivalente a usar un tipo Maybe o Option", lo que significa que también debe tener todas las mismas propiedades, incluida la pureza.

Cuando dije que levantar una excepción de hardware es "generalmente" clasificado como un efecto secundario, no siempre tiene que ser el caso. Por ejemplo, si la computadora en la que se ejecuta su código no llama a una interrupción cuando genera una excepción, sino que empuja un valor especial a la pila, entonces no es clasificable como no pura. Creo que el error NAN flotante IEEE se genera utilizando un valor especial y no una interrupción, por lo que cualquier excepción planteada al hacer matemáticas de coma flotante se puede clasificar como libre de efectos secundarios ya que el valor no se lee desde ningún estado global, pero sí una constante codificada en la FPU.

Mirando todos los requisitos para que un código pieza sea puro, las excepciones basadas en código y el azúcar sintáctico de la declaración throw marcan todas las casillas, no modifican ningún estado, no tienen ninguna interacción con sus funciones de llamada ni nada fuera de su invocación, y son referencialmente transparentes, pero solo una vez que el compilador ha cumplido con su código.

Al igual que todas las discusiones puras versus no puras, he excluido cualquier noción de tiempos de ejecución u operaciones de memoria y he operado bajo el supuesto de que cualquier función que PUEDA implementarse puramente se implementa de manera pura independientemente de su implementación real. Tampoco tengo evidencia del reclamo de excepción IANE punto flotante NAN.


La pureza solo se infringe si observa la excepción y toma una decisión basada en ella que cambia el flujo de control. En realidad, arrojar un valor de excepción es referencialmente transparente: es semánticamente equivalente a la no terminación u otros valores inferiores.

Si una función (pura) no es total , entonces se evalúa a un valor inferior. Cómo se codifica el valor inferior depende de la implementación; podría ser una excepción; o no terminación, o dividiendo por cero, o alguna otra falla.

Considera la función pura:

f :: Int -> Int f 0 = 1 f 1 = 2

Esto no está definido para todas las entradas. Para algunos, se evalúa abajo. La implementación codifica esto lanzando una excepción. Debería ser semánticamente equivalente a usar un tipo Maybe o Option .

Ahora, solo se rompe la transparencia referencial cuando se observa el valor inferior y se toman decisiones basadas en él, lo que podría introducir un no determinismo ya que pueden lanzarse muchas excepciones diferentes, y no se puede saber cuál. Por esta razón, la captura de excepciones se realiza en la mónada IO en Haskell, mientras que las llamadas excepciones "imprecisas" se pueden hacer de forma pura.

Por lo tanto, no es cierto que plantear una excepción sea un efecto secundario como tal. Es la cuestión de si se puede o no modificar el comportamiento de una función pura basada en un valor excepcional, rompiendo así la transparencia referencial.


La transparencia referencial también es la posibilidad de reemplazar un cálculo (por ejemplo, una invocación de función) con el resultado del cálculo en sí, algo que no se puede hacer si su función genera una excepción. ¡Esto se debe a que las excepciones no forman parte de la computación, pero deben ser atrapadas!