¿Son válidas las funciones del generador en la programación funcional?

15

Las preguntas son:

  • ¿Los generadores rompen el paradigma de programación funcional? ¿Por qué o por qué no?
  • En caso afirmativo, ¿pueden usarse los generadores en la programación funcional y cómo?

Considera lo siguiente:

function * downCounter(maxValue) {
  yield maxValue;
  yield * downCounter(maxValue > 0 ? maxValue - 1 : 0);
}

let counter = downCounter(26);
counter.next().value; // 26
counter.next().value; // 25
// ...etc

El método downCounter aparece sin estado. Además, llamar a downCounter con la misma entrada, siempre dará como resultado la misma salida. Sin embargo, al mismo tiempo, llamar a next() no produce resultados consistentes.

No estoy seguro de si los generadores rompen el paradigma de programación funcional porque en este ejemplo counter es un objeto generador y, por lo tanto, llamar a next() produciría los mismos resultados que otro objeto generador creado exactamente con el mismo maxValue .

Además, llamar a someCollection[3] en una matriz siempre devolverá el cuarto elemento. De manera similar, llamar a next() cuatro veces en un objeto generador también siempre devolverá el cuarto elemento.

Para más contexto, estas preguntas surgieron mientras trabajaban en un programación de kata . La persona que respondió a la pregunta, planteó la cuestión de si los generadores podrían usarse o no en la programación funcional y si se mantienen o no en estado.

    
pregunta Pete 29.09.2016 - 20:51
fuente

1 respuesta

12

Las funciones del generador no son particularmente especiales. Nosotros podemos implementar un mecanismo similar reescribiendo la función del generador en un estilo basado en devolución de llamada:

function downCounter(maxValue) {
  return {
    "value": maxValue,
    "next": function () {
      return downCounter(maxValue > 0 ? maxValue - 1 : 0);
     },
  };
}

let counter = downCounter(26);
counter.value; //=> 26
counter.next().value; //=> 25

Claramente, el downCounter es tan puro y funcional como puede ser. No hay problema aquí.

El protocolo generador usado por JavaScript involucra un objeto mutable. Esto no es necesario, ver el código anterior. En particular, los objetos mutables significan que perdemos transparencia referencial , la capacidad de reemplazar una expresión por su valor. Mientras que en mi ejemplo, counter.next().value siempre evaluará a 25 sin importar dónde ocurra y con qué frecuencia lo repetimos, este no es el caso con el generador JS: en un momento dado es 26 , luego 25 , y realmente podría ser cualquier número. Esto es problemático si pasamos una referencia al generador a otra función:

counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()

Claramente, los generadores mantienen el estado y, por lo tanto, no son adecuados para la programación funcional "pura". Afortunadamente, no tiene que hacer una programación puramente funcional, y puede ser pragmático. Si los generadores hacen que su código sea más claro, debe usarlos sin mala conciencia. Después de todo, JavaScript no es un lenguaje funcional puro, a diferencia de, por ejemplo, Haskell.

Por cierto, en Haskell no hay diferencia entre devolver una lista y un generador, ya que utiliza una evaluación perezosa:

downCounter :: Int -> [Int]
downCounter maxValue =
  maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
    
respondido por el amon 29.09.2016 - 22:02
fuente

Lea otras preguntas en las etiquetas