25

He estado trabajando con node.js durante un tiempo en una aplicación de chat (lo sé, muy original, pero pensé que sería un buen proyecto de aprendizaje). Underscore.js proporciona una gran cantidad de conceptos de programación funcional que se ven interesantes, así que me gustaría entender cómo se configuraría un programa funcional en JavaScript.¿Cómo se presenta una aplicación de JavaScript funcional basada en programación?

De mi comprensión de la programación funcional (que puede estar equivocado), la idea es evitar efectos secundarios, que son, básicamente, tiene una función que actualiza otra variable fuera de la función así que algo como

var external; 
function foo() { 
    external = 'bar'; 
} 
foo(); 

estaría creando un efecto secundario, ¿correcto? Por lo tanto, como regla general, desea evitar variables perturbadoras en el alcance global.

Ok, entonces, ¿cómo funciona eso cuando se trata de objetos y qué no? Por ejemplo, muchas veces, voy a tener un constructor y un método init que inicializa el objeto, así:

var Foo = function(initVars) { 
    this.init(initVars); 
} 

Foo.prototype.init = function(initVars) { 
    this.bar1 = initVars['bar1']; 
    this.bar2 = initVars['bar2']; 
    //.... 
} 

var myFoo = new Foo({'bar1': '1', 'bar2': '2'}); 

efectos secundarios Así que mi método init se causen intencionalmente, pero lo que sería una forma funcional para manejar el mismo tipo de situación?

Además, si alguien pudiera indicarme un código fuente de Python o JavaScript de un programa que intenta ser lo más funcional posible, eso también sería muy apreciado. Siento que estoy cerca de "conseguirlo", pero simplemente no estoy allí. Principalmente estoy interesado en cómo funciona la programación funcional con el concepto tradicional de clases OOP (o elimina algo diferente si ese es el caso).

+0

Solo quería señalar que ahora me doy cuenta de que un método Foo.init realmente no tiene sentido si estás tratando de cumplir con la programación funcional, porque la única razón que tendría para un método init sería si Quería volver a inicializar un objeto Foo después ... lo cual es totalmente contrario a la idea de inmutabilidad. Entonces, en lugar de eso, debería hacer lo que sea necesario para inicializar el objeto en el constructor, y la próxima vez que necesite actualizar el objeto Foo o reiniciarlo, solo hago una nueva instancia de Foo y trato con eso. ¡Todo comienza a tener sentido! – user126715

Respuesta

26

Debe leer esta pregunta:

Javascript as a functional language

Hay un montón de enlaces de interés, entre ellos:

Ahora, para mi opinión. Mucha gente misunderstand JavaScript, posiblemente porque su sintaxis se parece a la mayoría de otros lenguajes de programación (donde Lisp/Haskell/OCaml se ven completamente diferentes). JavaScript es no orientado a objetos, en realidad es un prototype-based language. No tiene clases ni herencia clásica, por lo que no debería compararse con Java o C++.

JavaScript puede ser mejor en comparación con un Lisp; tiene cierres y funciones de primera clase. Utilizándolos puede crear otras técnicas de programación funcional, como partial application (currying).

Tomemos un ejemplo (usando sys.puts de Node.js):

var external; 
function foo() { 
    external = Math.random() * 1000; 
} 
foo(); 

sys.puts(external); 

para deshacerse de los efectos secundarios globales, podemos envolverlo en un cierre:

(function() { 
    var external; 
    function foo() { 
     external = Math.random() * 1000; 
    } 
    foo(); 

    sys.puts(external); 
})(); 

Observe que hemos no puede hacer nada con external o foo fuera del alcance. Están completamente envueltos en su propio cierre, intocables.

Ahora, para deshacerse de la external efecto secundario:

(function() { 
    function foo() { 
     return Math.random() * 1000; 
    } 

    sys.puts(foo()); 
})(); 

Al final, el ejemplo no es puramente funcional, ya que no puede ser. Usar un número aleatorio lee desde el estado global (para obtener una semilla) e imprimir en la consola es un efecto secundario.

También quiero señalar que la combinación de programación funcional con objetos está perfectamente bien. Tome esto por ejemplo:

var Square = function(x, y, w, h) { 
    this.x = x; 
    this.y = y; 
    this.w = w; 
    this.h = h; 
}; 

function getArea(square) { 
    return square.w * square.h; 
} 

function sum(values) { 
    var total = 0; 

    values.forEach(function(value) { 
     total += value; 
    }); 

    return total; 
} 

sys.puts(sum([new Square(0, 0, 10, 10), new Square(5, 2, 30, 50), new Square(100, 40, 20, 19)].map(function(square) { 
    return getArea(square); 
}))); 

Como puede ver, usar objetos en un lenguaje funcional puede estar bien. Algunos Lisp incluso tienen cosas llamadas listas de propiedades que pueden considerarse objetos.

El verdadero truco para usar objetos en un estilo funcional es asegurarse de que no confíe en sus efectos secundarios sino que los trate como inmutables. Una forma fácil es siempre que desee cambiar una propiedad, simplemente cree un objeto nuevo con los nuevos detalles y páselo, (este es el enfoque que se usa a menudo en Clojure y Haskell).

Creo firmemente que los aspectos funcionales pueden ser muy útiles en JavaScript, pero en última instancia, debe usar lo que sea que haga que el código sea más legible y lo que funcione para usted.

+0

+1 buena respuesta. – Anurag

+1

gracias por su respuesta y los enlaces útiles. Realmente me gusta la idea de simplemente pasar nuevos objetos a mis funciones y hacer que actúen en consecuencia. Eso fue un aha! momento para mi Lo único que todavía estaba luchando con fp en general, es mantener la funcionalidad junto a código sabio. Con OO, es fácil porque casi todo es un método fuera de la clase (es decir, car.startEngine(); car.applyBrakes();). Con FP, creo que puedo hacer algo similar al usar un espacio de nombres y simplemente tener mis métodos requieren un objeto como primer argumento. entonces var car = {}; car.startEngine = function (carObj) {} – user126715

+1

@ user321521, ¿por qué no poner la función en el prototipo del objeto que le pasas? La separación de datos de las operaciones no hace que el código sea más o menos FP. Es bastante ortogonal. – Alexey

0

SO2 cosas a señalar,

  1. En el primer ejemplo la variable no se filtra en la zona global y es la forma en que se debe hacer, trate de no utilizar variables sin tener que hacerlos es decir, test = 'data' hará que los datos se filtren en el área global.

  2. Su segundo ejemplo es correcto también, bar1 y bar2 solo se declararán en el objeto Foo.

cosas a tener en mente tratar de no abusar de prototipos ya que se aplica a todos los objetos que se crea, esto podría ser muy intensivo de la memoria en función de la complejidad de sus objetos son.

Si está buscando un marco de desarrollo de aplicaciones, eche un vistazo a ExtJs. Personalmente, creo que encajaría perfectamente en el modelo contra el que intentas desarrollar. Simplemente tenga en cuenta cómo funciona su modelo de licencia antes de invertir fuertemente en él.

+0

gracias por el comentario. node.js es javascript del lado del servidor, así que no creo que extJS esté destinado a funcionar allí, pero puedo estar equivocado. Estoy más interesado en las mejores prácticas para aplicar el modelo de programación funcional a JavaScript en general. Sé cómo construir la aplicación si solo uso el diseño tradicional orientado a objetos (sin embargo, un buen punto para la creación de prototipos), pero quiero saber cómo atacarlo utilizando los conceptos de programación funcional tanto como pueda. – user126715

2

Creo que, http://documentcloud.github.com/underscore/ debe ser adecuado para lo que necesita, proporciona las funciones de orden superior más importantes para la programación funcional y no tiene funciones del lado del cliente para la manipulación del DOM que no necesita para el servidor. Aunque no tengo experiencia con eso.

Como nota al margen: característica principal de la función de programación es Referential transparency de una función - el resultado de la función depende únicamente de sus parámetros - la función no depende de los cambios en otros objetos y no introduce ningún cambio excepto su valor de resultado. Hace que sea fácil razonar sobre la corrección del programa y muy valioso para la implementación de múltiples hilos confiables (si es relevante).Aunque JavaScript no es el lenguaje de apuestas para FP, espero que las estructuras de datos inmutables sean muy costosas en cuanto a rendimiento.

+1

Sí, estoy usando underscore.js actualmente y lo he usado para eliminar todos mis bucles for hasta ahora. Esa es la clave de FP, tal como yo lo veo también. La idea de simplemente pasar nuevos objetos a las funciones mencionadas por Brian a continuación realmente abrió un bloque que estaba teniendo sobre cómo lidiar con él. Veré cómo funciona. Node.js/V8 parece muy rápido por todo lo que he leído hasta ahora, por lo que será interesante ver si esta técnica también funciona. – user126715

5

Debe comprender que la programación funcional y la programación orientada a objetos son algo antiéticas entre sí. No es posible que ambos sean purely functional y solo orientado a objetos.

Functional programming es todo acerca de computaciones sin estado. Object oriented programming es todo acerca de las transiciones de estado. (Paraphasing this. Esperemos que no demasiado)

JavaScript está más orientado a objetos de lo que es funcional. Lo que significa que si desea programar en un estilo puramente funcional, debe renunciar a partes grandes del idioma. Específicamente todas las partes orientadas a objetos.

Si está dispuesto a ser más pragmático al respecto, hay algunas inspiraciones del mundo puramente funcional que podría utilizar.

trato de cumplir con las siguientes reglas:

funciones que realizan los cálculos no deben alterar el estado. Y las funciones que alteran el estado no deben realizar cálculos. Además, las funciones que alteran el estado deben modificar el estado más pequeño posible. El objetivo es tener muchas pequeñas funciones que solo hacen una cosa. Entonces, si necesita hacer algo grande, debe componer una serie de pequeñas funciones para hacer lo que necesita.

Hay una serie de beneficios que se pueden obtener a partir de las siguientes reglas:

  1. Facilidad de reutilización. Cuanto más larga y compleja es una función, más especializada también es, y por lo tanto, es menos probable que se pueda reutilizar. La implicación inversa es que las funciones más cortas tienden a ser más genéricas y, por lo tanto, más fáciles de reutilizar.

  2. Fiabilidad del código. Es más fácil razonar sobre la corrección del código si es menos complejo.

  3. Es más fácil probar funciones cuando solo hacen una cosa. De esta forma hay menos casos especiales para probar.

Actualización:

sugerencia Incorporated de hacer comentarios.

Actualización 2:

añadido algunos enlaces útiles.

+3

No estoy de acuerdo. JavaScript es realmente más funcional que orientado a objetos. Tiene cierres, funciones de primera clase, carece de clases y herencia. Desde el punto de vista sintáctico, se parece más a Java que a Lisp, pero en general comparte más en común con este último. –

+1

Estás equivocado: JavaScript tiene ambas clases y herencia (pero quizás no en el estilo al que estás acostumbrado). Y la mayoría si sus estructuras de datos son mutables, lo que lo diferencia de todo el lenguaje puramente funcional. Estoy totalmente en desacuerdo con la idea de que se parece más a Lisp que a Java. – KaptajnKold

+0

Adam, estoy muy de acuerdo con las reglas que estableces. también sería valioso agregar motivos (o vínculos) por los cuales las reglas son realmente valiosas. En mi humilde opinión: 1. fácil de reutilizar el código 2. fiabilidad del código (fácil de razonar sobre la corrección del código) 3. fácil de probar el código – Alexey

Cuestiones relacionadas