En una publicación de blog en F # por diversión y ganancias, dice:
En un diseño funcional, es muy importante separar el comportamiento de los datos. Los tipos de datos son simples y "tontos". Y luego, por separado, tiene una serie de funciones que actúan sobre esos tipos de datos.
Esto es exactamente lo contrario de un diseño orientado a objetos, donde el comportamiento y los datos están destinados a combinarse. Después de todo, eso es exactamente lo que es una clase. De hecho, en un diseño verdaderamente orientado a objetos, no debe tener nada más que comportamiento: los datos son privados y solo se puede acceder a ellos mediante métodos.
De hecho, en OOD, no tener un comportamiento suficiente en torno a un tipo de datos se considera algo malo e incluso tiene un nombre: el " modelo de dominio anémico ".
Dado que en C # parece que seguimos pidiendo prestado de F # y tratando de escribir más código de estilo funcional; ¿Cómo es que no tomamos prestada la idea de separar datos / comportamiento, e incluso lo consideramos malo? ¿Es simplemente que la definición no coincide con OOP, o hay una razón concreta de que sea mala en C # que por alguna razón no se aplica en F # (y de hecho, se invierte)?
(Nota: Estoy específicamente interesado en las diferencias en C # / F # que podrían cambiar la opinión de lo que es bueno / malo, en lugar de las personas que pueden estar en desacuerdo con cualquiera de las opiniones en la publicación del blog).
fuente
Respuestas:
La razón principal por la que FP apunta a esto y C # OOP no lo hace es que en FP el foco está en la transparencia referencial; es decir, los datos entran en una función y salen datos, pero los datos originales no cambian.
En C # OOP existe un concepto de delegación de responsabilidad en el que delega la administración de un objeto en él y, por lo tanto, desea que cambie sus propios elementos internos.
En FP nunca desea cambiar los valores en un objeto, por lo tanto, tener sus funciones incrustadas en su objeto no tiene sentido.
Además, en FP tiene un polimorfismo de tipo más alto que permite que sus funciones sean mucho más generalizadas de lo que permite C # OOP. De esta manera, puede escribir una función que funcione para cualquiera
a
y, por lo tanto, tenerla incrustada en un bloque de datos no tiene sentido; eso acoplaría estrechamente el método para que solo funcione con ese tipo particular dea
. Un comportamiento como ese está muy bien y es común en C # OOP porque no tienes la capacidad de abstraer funciones tan generalmente de todos modos, pero en FP es una compensación.El mayor problema que he visto en los modelos de dominio anémico en C # OOP es que terminas con un código duplicado porque tienes DTO x, y 4 funciones diferentes que comprometen la actividad f a DTO x porque 4 personas diferentes no vieron la otra implementación . Cuando coloca el método directamente en DTO x, esas 4 personas ven la implementación de f y la reutilizan.
Los modelos de datos anémicos en C # OOP dificultan la reutilización del código, pero este no es el caso en FP porque una sola función se generaliza en tantos tipos diferentes que obtienes una mayor reutilización del código, ya que esa función es utilizable en muchos más escenarios que una función escribiría para un solo DTO en C #.
Como se señaló en los comentarios , la inferencia de tipos es uno de los beneficios en los que se basa FP para permitir un polimorfismo tan significativo, y específicamente puede rastrear esto al sistema de tipos Hindley Milner con la inferencia de tipo Algorithm W; dicha inferencia de tipo en el sistema de tipo C # OOP se evitó porque el tiempo de compilación cuando se agrega inferencia basada en restricciones se vuelve extremadamente largo debido a la búsqueda exhaustiva necesaria, detalles aquí: https://stackoverflow.com/questions/3968834/generics-why -cant-the-compiler-infer-the-type-argumentos-en-este-caso
fuente
Su pregunta tiene un gran problema que limitará la utilidad de las respuestas que obtiene: implica / asume que F # y FP son similares. FP es una gran familia de lenguajes que incluye reescritura de términos simbólicos, dinámicos y estáticos. Incluso entre los lenguajes FP de tipo estático, existen muchas tecnologías diferentes para expresar modelos de dominio, como los módulos de orden superior en OCaml y SML (que no existen en F #). F # es uno de estos lenguajes funcionales, pero es particularmente notable por ser delgado y, en particular, no proporciona módulos de orden superior ni tipos de tipo superior.
De hecho, no podría comenzar a decirte cómo se expresan los modelos de dominio en FP. La otra respuesta aquí habla muy específicamente sobre cómo se hace en Haskell y no es aplicable en absoluto a Lisp (la madre de todos los idiomas FP), la familia de idiomas ML o cualquier otro idioma funcional.
Los genéricos pueden considerarse una forma de separar datos y comportamiento. Los genéricos provienen de la familia ML de lenguajes de programación funcionales que no forman parte de OOP. C # tiene genéricos, por supuesto. Entonces, uno podría argumentar que C # está prestando lentamente la idea de separar datos y comportamiento.
Creo que OOP se basa en una premisa fundamentalmente diferente y, en consecuencia, no le brinda las herramientas que necesita para separar los datos y el comportamiento. Para todos los fines prácticos, necesita los tipos de datos de producto y suma y el envío a través de ellos. En ML esto significa unión y tipos de registros y coincidencia de patrones.
Mira el ejemplo que di aquí .
Tenga cuidado al saltar de OOP a C #. C # no es tan puritano sobre OOP como otros lenguajes. .NET Framework ahora está lleno de genéricos, métodos estáticos e incluso lambdas.
La falta de tipos de unión y coincidencia de patrones en C # hace que sea casi imposible hacerlo. Cuando todo lo que tienes es un martillo, todo parece un clavo ...
fuente
Creo que en una aplicación empresarial a menudo no desea ocultar datos porque la coincidencia de patrones en valores inmutables es excelente para garantizar que cubra todos los casos posibles. Pero si está implementando algoritmos complejos o estructuras de datos, es mejor que oculte los detalles de implementación convirtiendo los ADT (tipos de datos algebraicos) en ADT (tipos de datos abstractos).
fuente