Programación funcional para efectos secundarios de bucle.

8

Estoy tratando de entender por qué tener una variable local o un bucle for dentro de una función no se considera una programación puramente funcional.

Dada esta función:

int as_int(char *str)
{
    int acc; /* accumulate the partial result */

    for (acc = 0; isdigit(*str); str++) {
        acc = acc * 10 + (*str - '0');
    }

    return acc;
}

¿En qué circunstancias la variable acc sería un efecto secundario? Incluso en un entorno concurrente, cada invocación de la función tendría su propia copia de acc. Así que no entiendo por qué no está permitido en la programación funcional.

tomateRadar
fuente
3
No hay efectos secundarios. Ver: programmers.stackexchange.com/questions/196112/…
Ben Aaronson
55
as_intes una función pura, pero el código que contiene no es puro.
Doval
1
El código puro generalmente se asocia con variables inmutables. acces mutable
Florian Margaine

Respuestas:

11

Looping en la programación funcional no se hace con las sentencias de control como fory while, se hace con llamadas explícitas a funciones como map, foldo recursividad - todos los cuales implican la colocación de la llamada bucle interno dentro de otra función . Si el código del bucle muta variables fuera del bucle, esta función del bucle interno estaría manipulando variables fuera de su alcance y, por lo tanto, sería impuro . Entonces, toda la función externa es pura, pero el bucle no lo es. Las construcciones de bucle en la programación funcional requieren que haga explícito el estado. Traducir su código a algo utilizando herramientas de bucle de programación funcional revela la impureza:

int as_int(char *str)
{
    int acc = 0; /* accumulate the partial result */

    map(takeWhile(isdigit, str), void function(char *chr) {
      acc = acc * 10 + (chr - '0');
    });

    return acc;
}

(Nota: esta sintaxis es aproximada para transmitir la idea general)

Este código usa una función interna para el cuerpo del bucle que debe mutar la variable acc, que está fuera de su alcance. Esto es impuro: la función del bucle interno depende del contexto del bucle externo , llamarlo varias veces con el mismo carácter tendrá efectos secundarios, y el orden en que se llama en la secuencia de caracteres es importante. En la programación funcional, para hacer de esto una función pura, tendría que hacer explícita esta dependencia del estado pasado entre iteraciones de bucle con fold:

int as_int(char *str)
{
    return fold(takeWhile(isdigit, str), 0, int function(char *chr, int acc) {
      return acc * 10 + (chr - '0');
    });
}

foldutiliza una función de dos argumentos para el cuerpo del bucle interno: el primer argumento es un elemento de la secuencia que foldse repite, mientras que el segundo es algún valor que el cuerpo del bucle interno utiliza para generar resultados parciales. Para la primera iteración del bucle, acces 0, para el segundo, acces lo que sea que haya devuelto la primera llamada de función del bucle interno, para el tercero, sea lo que haya devuelto el segundo bucle interno, y el bucle final devuelve el resultado de toda la foldexpresión.

Tenga en cuenta que esto no es realmente un problema con su código desde la perspectiva del resto de su programa: ambas definiciones as_intson puras. La diferencia es que al hacer que el código del bucle interno sea una función pura, puede aprovechar la gran variedad de herramientas que ofrece la programación funcional para descomponer el bucle en algo más declarativo (por ejemplo, usando takeWhile, fold, filter, map, etc., etc.)

Jack
fuente
66
+1. Entre las ventajas de utilizar las funciones de orden superior tales como takeWhile, fold, filter, map, (es decir, estilo declarativo) es que también dejar de pensar en términos de "Calcular algo por las posiciones de memoria de actualización destructiva". De esta manera, el resultado no depende del historial / secuencia exacta de los pasos de cálculo.
Giorgio