Pregunta

Necesito ayuda para entender la diferencia entre mi noción actual de estado OOP y la forma en que se haría en un lenguaje funcional como Haskell o Clojure.

Para usar un ejemplo trillado, digamos que estamos tratando con objetos / estructuras / cuentas simplificadas de cuentas bancarias. En un lenguaje OOP, tendría alguna clase con una referencia a una BankAccount, que tendría variables de instancia para cosas como la tasa de interés y métodos como setInterestRate () que cambian el estado del objeto y generalmente no devuelven nada. En Clojure, por ejemplo, tendría una estructura de cuenta bancaria (un hashmap glorificado) y funciones especiales que toman un parámetro de cuenta bancaria y otra información, y devuelven una nueva estructura. Entonces, en lugar de cambiar el estado del objeto original, ahora tengo uno nuevo que se devuelve con las modificaciones deseadas.

Entonces ... ¿qué hago con él? ¿Sobrescribir la variable que hacía referencia a la antigua cuenta bancaria? Si es así, ¿eso tiene ventajas sobre el enfoque OOP que cambia el estado? Al final, en ambos casos parece que uno tiene una variable que hace referencia al objeto con los cambios necesarios. Retrasado como soy, solo tengo un concepto vago de lo que está sucediendo.

Espero que tenga sentido, ¡gracias por cualquier ayuda!

¿Fue útil?

Solución

En un estilo funcional puro, nunca sobrescribirá ninguna variable.

Una analogía sería con el espacio-tiempo en física. Si considera el mundo como 3d, entonces los objetos no tienen posiciones fijas, se mueven con el tiempo. Para que las matemáticas influyan en el mundo físico, por lo tanto, agregamos una dimensión de tiempo y consideramos los valores de varias propiedades en momentos particulares. Al hacerlo, hemos convertido los objetos de nuestro estudio en constantes. De manera similar, en la programación, existe una simplicidad conceptual al trabajar con valores inmutables. Los objetos con identidad en el mundo real pueden modelarse como una secuencia de valores inmutables (los estados del objeto en momentos crecientes) en lugar de como un valor único que cambia.

Por supuesto, los detalles de cómo asociar la secuencia de valores a una & "; identidad de objeto &"; puede ser un poco peludo Haskell tiene Mónadas que le permiten modelar el estado. Programación funcional reactiva es un intento más literal de modelar objetos en el mundo con actualizaciones funcionales puras, que yo pensar es una dirección muy prometedora para la programación.

Notaré que Clojure, a diferencia de Haskell, no es puro, y puede actualizar las variables como sugirió. Si solo está actualizando algunas variables a un nivel alto, probablemente todavía disfrutará de muchos de los beneficios de simplicidad conceptual de la programación funcional.

Otros consejos

Presumiblemente en el mundo OO usted tiene un ciclo y está modificando estas cuentas bancarias una y otra vez en respuesta a las solicitudes. Supongamos que tiene una cartera completa de cuentas y éstas tienen tipo Cartera. Luego, en Haskell, escribirías una función pura

updatePortfolio :: Request -> Portfolio -> Portfolio

Y su ciclo principal puede leer las solicitudes de entrada estándar y mantener su cartera actualizada. (El ejemplo no sirve de mucho a menos que también pueda escribir la cartera, pero es más simple).

readRequest :: IO Request  -- an action that, when performed, reads a Request with side effects

main :: Portfolio -> IO ()  -- a completely useless program that updates a Portfolio in response to a stream of Requests

main portfolio = do req <- readRequest
                    main (updatePortfolio req)

y ahora espero que vea lo que sucedió con su estado mutable: en un programa funcional típico, el estado que los cambios se pasan como un parámetro a una función. Cuando el estado cambia, realiza una nueva llamada de función. La llamada está en posición de cola (puede buscar 'llamada de cola adecuada') y, por lo tanto, no utiliza ningún recurso adicional y, de hecho, cuando el compilador genera código de ensamblaje genera un bucle y mantendrá el puntero hacia el Portafolio siempre cambiante en un registro.

Este es un ejemplo de juguete, pero espero que te dé un poco del sabor de un lenguaje funcional.

Entonces ... ¿qué hago con él? ¿Sobrescribir la variable que hacía referencia a la antigua cuenta bancaria?

Si es así, ¿eso tiene ventajas sobre el enfoque OOP que cambia el estado?

Digamos que el cálculo de cualquier acción que realice en esa estructura lleva mucho tiempo y algo sucede a mitad de camino y necesita volver a la estructura original o el cálculo provocó un error. Con la interpretación que me ha presentado de OO (usando una referencia, porque puede tener un lenguaje OO inmutable), los datos podrían estar corruptos; se desconoce a menos que se proporcione suficiente información de la llamada de función fallida y sugiera que falló mal. En un enfoque funcional, usted sabe con certeza que su estructura de datos original es correcta, porque inicialmente hizo una copia.

Amplíe este escenario en aplicaciones multiproceso. Podemos asegurarnos de que nadie más esté utilizando la estructura de datos que somos, ya que todos tenemos nuestra propia versión.

Además, podemos ahorrar espacio utilizando datos de la otra estructura desde la que estamos copiando. Un ejemplo clásico es cuando se agrega un elemento al encabezado de una lista. Si tenemos un puntero al segundo elemento, y un puntero al primer elemento, podemos hacer referencia a ambas listas con solo el tamaño del primero (ver más abajo). Sin inmutabilidad no podemos garantizar esto.

        b__
           |  
a -> [6|] -+-> [5|] -> [4|] -> [3|] -> [2|] -> [1|x]

Mire a Haskell, que es un lenguaje funcional puro & # 8212; no tiene reasignación de ningún tipo, así como tampoco tiene otros efectos secundarios: para hacer IO, en el IO monad construir realmente reemplaza el RealWorld con una nueva instancia del mundo que tiene, por ejemplo, texto nuevo en la consola.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top