scala programming-languages functional-programming paradigms jvm-languages

Eficacia de atenerse solo al paradigma funcional en Scala



programming-languages functional-programming (4)

Aunque comencé a programar en scala, definitivamente no soy un miembro más experimentado de la comunidad de scala. Pero he trabajado durante mucho tiempo con otro idioma: python, que en menor medida tiene una situación similar. Es compatible con los estilos de procedimiento, orientado a objetos y funcional. Descubrí que si bien algunas personas tienden a limitarse a un extremo, muchas combinan construcciones de diferentes estilos. Por lo tanto, python idiomático consistirá en un uso intensivo de las listas de comprensión, primera clase, orden superior y funciones parciales, incluso cuando los datos sobre los que trabajan estas funciones son objetos. Sin embargo, hay que señalar que a lo largo de los años, la tendencia ha sido cambiar ligeramente hacia lo funcional.

Creo que dada la inclusividad de los estilos que soporta Scala, solo es probable que no haya una manera correcta de resolver un problema. Pero no me sorprendería ver que una mayor proporción de programadores tiende lentamente hacia un estilo que hace un uso juicioso de ambos, incluso cuando siguen siendo tolerantes con los programadores que desean atenerse a uno de los extremos.

Recientemente compré Programación Scala y la he estado leyendo. ¡El idioma definitivamente no es lo que esperaba! Específicamente, parece que implementa casi todas las ideas de lenguaje de programación que conozco, fuera de las macros de Lisp y la segregación de efectos secundarios de nivel de tipo de Haskell.

Francamente, me tiene algo abrumado. Aunque supongo que es bueno tener tantas herramientas a mi disposición, realmente estaba buscando un lenguaje funcional fuertemente tipado en la JVM. Me imagino que probablemente podría usar Scala de esa manera, pero me imagino que si interactúo con alguna de las bibliotecas o utilizo el código de cualquier otra persona, me encontraré con muchas de estas cosas avanzadas (para mí) OOP: rasgos y "jerarquía de objetos linealización, "todos estos objetos abstractos y primordiales de negocios, singleton, paquete y complementarios, conversiones implícitas ... sin mencionar los diversos métodos abreviados y azúcares sintácticos.

Las personas a menudo se quejan de los programadores que intentan adaptar el estilo de un idioma a otro, por muchas buenas razones. Pero no todos los idiomas son tan multi-paradigmas como Scala, entonces ¿tal vez su comunidad tiene una visión diferente? En F #, por ejemplo, parece haber cierto margen de maniobra en los estilos de programación y la cantidad de POO que utiliza. Pero solo por leer, no estoy seguro si esta también es una buena filosofía para Scala.

¿Pueden los programadores de Scala más experimentados ayudarme aquí? Edite para mayor claridad: Básicamente, ¿puedo usar de manera segura solo (o en su mayoría) las funciones de PF de Scala sin preocuparme por su lado OOP avanzado?

Perdón por la pregunta divagante!


Creo que ciertamente hay un punto sólido en esta pregunta. Se ilumina al observar cómo una biblioteca puede manejar las excepciones .

Si interactúa con una biblioteca de Java, cualquier método puede lanzar una excepción. Esto podría ser explícitamente, a través de una excepción marcada , o de forma transparente (desde una perspectiva de API), a través de una excepción de tiempo de ejecución . ¿Qué se supone que debes hacer al respecto, como un programador que podría estar tratando de usar el tipo más funcional para indicar el fallo (o la Validation Scalaz )?

No me queda nada claro cómo se desarrollará esto en la escala (es decir, abordar el problema y elegir el enfoque puramente funcional). Ciertamente, es probable que produzca muchos estilos diferentes de codificación. Dicho esto, hay muchas cosas que son ricas y útiles a lo largo del lado funcional de scala que puede elegir; y, sin duda, es posible que esta combinación de código funcional e imperativo en su programa funcione al mismo tiempo. Aunque un purista funcional puede, por supuesto, estar en desacuerdo sobre si esta es una situación óptima.

Para lo que vale, he encontrado que la aplicación de paradigmas funcionales dentro de mis programas ha mejorado mi código sin fin. Como no he probado Haskell ni F # , no puedo decirle si el resultado neto es mejor o peor.

Pero limpia el piso con Java; y, desde mi perspectiva, obtener eso en la JVM (con la ventaja práctica de coexistir con todas nuestras bibliotecas de Java) es una aplicación excelente.

En los bordes, a medida que comience a utilizar más el lado funcional de scala, hay una serie de problemas con los que se encontrará. Estos son los más obvios:

  • la falta de función implícita de curry (es decir, equivalencia de A => B => C y (A, B) => C )
  • la falta de tuplos de funciones implícitas (es decir, la equivalencia entre una función n-aria y una función 1-aria que toma una n-tupla como parámetro)
  • la falta de inferencia para constructores de tipo de aplicación parcial (es decir, la equivalencia de M[A] con, por ejemplo, Either[Int, A] )

Ambas cuestiones hacen que el código funcional sea bonito y claro, feo y oscuro.


Permítanme agregar algunos comentarios a la respuesta de Kevin.

Los rasgos se utilizan principalmente como abstracciones (en términos generales, de la misma manera en que las clases de tipo Haskell definen abstracciones) y mixins. Por lo tanto, mi código podría usar el rasgo del mapa, pero en realidad está usando una instancia de tipo como HashMap. A la inversa, si quiero que mi tipo de "Servicio" tenga funcionalidad de registro, podría mezclar un rasgo de Registro reutilizable.

Afortunadamente, rara vez tiene que pensar en el algoritmo de linealización más allá de los casos simples, por ejemplo, puedo mezclar un rasgo que intercepta (ajusta) una llamada de método para registrar el hecho de que se invocó el método. El algoritmo debe manejar los casos de linealización más complicados (como los ejemplos que mostramos en el libro), pero en realidad esos tipos complicados son un diseño deficiente, en mi humilde opinión.

Los Singletons, incluidos los objetos complementarios de subconjuntos especiales, están diseñados para hacer de todo un objeto, pero también podría verlos como funciones de ajuste de espacios de nombres y posiblemente algún estado.

Las conversiones implícitas son muy útiles, aunque un poco "mágicas". Puede que le resulte útil ver cómo se usan para simular las clases de tipos de Haskell. Aquí hay una gran publicación de Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html


Una de las cosas que está viendo es que Scala es, sobre todo, un lenguaje funcional fuertemente tipado en la JVM.

Scala no es solo un lenguaje funcional. Comenzó como uno (Funnel: http://lamp.epfl.ch/funnel/ ), pero luego se amplió para convertirlo en un lenguaje orientado a objetos, con el objetivo explícito de hacerlo muy compatible con las clases de Java.

la abstracción y la anulación y los paquetes son todos ejemplos de esta interoperabilidad en acción.

El resto podría considerarse no como nuevas características, sino simplemente como eliminar restricciones de Java. Tomándolos uno a la vez:

Caracterización y linealización de la jerarquía de objetos.

Elimina la restricción de que las interfaces Java solo pueden contener métodos abstractos. La linealización es la forma en que Scala resuelve los problemas de herencia de diamante que esto causaría.

singletons

Los métodos estáticos de Java son una resaca de su herencia de sintaxis C ++, que a su vez los agregó para apoyar mejor el estilo de procedimiento necesario para interoperar con C. Los métodos estáticos NO están orientados a objetos (vea esta pregunta: ¿Por qué los objetos singleton están más orientados a objetos?) ? )

objetos complementarios

Permitir el uso de singletons en lugar de métodos estáticos, con sus derechos de acceso privilegiados.

conversiones implícitas

Java ya lo hace, pero está restringido solo a la conversión implícita de objetos y primitivos a cadenas, como en la expresión "" + 3 . Scala solo extiende esta idea y permite que el programador la use para otras conversiones.