programacion funcional desventajas caracteristicas scala haskell f# functional-programming anti-patterns

scala - desventajas - programacion funcional



¿Hay algún anti-patrón documentado para la programación funcional? (1)

El único anti-patrón que he visto es la sobre-monadización, y como las mónadas pueden ser increíblemente útiles, esto se encuentra en algún lugar entre una mala práctica y un anti-patrón.

Supongamos que tienes alguna propiedad P que quieres que sea cierta para algunos de tus objetos. Puedes decorar tus objetos con una mónada P (aquí en Scala, usa paste en el REPL para que el objeto y su compañero se peguen):

class P[A](val value: A) { def flatMap[B](f: A => P[B]): P[B] = f(value) // AKA bind, >>= def map[B](f: A => B) = flatMap(f andThen P.pure) // (to keep `for` happy) } object P { def pure[A](a: A) = new P(a) // AKA unit, return }

Bien, hasta ahora todo bien; Hicimos trampa un poco al hacer que el value un value en lugar de hacerlo un comonad (si eso es lo que queríamos), pero ahora tenemos una envoltura práctica en la que podemos envolver cualquier cosa. Ahora supongamos que también tenemos propiedades Q y R

class Q[A](val value: A) { def flatMap[B](f: A => Q[B]): Q[B] = f(value) def map[B](f: A => B) = flatMap(f andThen Q.pure) } object Q { def pure[A](a: A) = new Q(a) } class R[A](val value: A) { def flatMap[B](f: A => R[B]): R[B] = f(value) def map[B](f: A => B) = flatMap(f andThen R.pure) } object R { def pure[A](a: A) = new R(a) }

Así que decoramos nuestro objeto:

class Foo { override def toString = "foo" } val bippy = R.pure( Q.pure( P.pure( new Foo ) ) )

Ahora nos encontramos de repente con una gran cantidad de problemas. Si tenemos un método que requiere la propiedad Q , ¿cómo llegamos a él?

def bar(qf: Q[Foo]) = qf.value.toString + "bar"

Bueno, claramente bar(bippy) no va a funcionar. Existen operaciones de swap o swap que efectivamente voltean las mónadas, por lo que podríamos, si hubiéramos definido el swap de una manera apropiada, hacer algo como

bippy.map(_.swap).map(_.map(bar))

para recuperar nuestra cadena (en realidad, una R[P[String]] ). Pero ahora nos hemos comprometido a hacer algo como esto para cada método que llamamos.

Esto suele ser lo incorrecto para hacer. Cuando sea posible, debe usar algún otro mecanismo de abstracción que sea igualmente seguro. Por ejemplo, en Scala también puedes crear rasgos de marcador.

trait X trait Y trait Z val tweel = new Foo with X with Y with Z def baz(yf: Foo with Y) = yf.toString + "baz" baz(tweel)

¡Uf! Mucho más fácil. Ahora es muy importante señalar que no todo es más fácil . Por ejemplo, con este método, si empiezas a manipular a Foo , tendrás que seguir la pista de todos los decoradores en lugar de dejar que el map / map flatMap haga por ti. Pero muy a menudo no es necesario hacer un montón de manipulaciones en especie, y luego las mónadas profundamente anidadas son un antipatrón.

(Nota: el anidamiento monádico tiene una estructura de pila mientras que los rasgos tienen una estructura de conjunto; no hay ninguna razón inherente para que un compilador no permita monadas de conjunto, pero no es una construcción natural para formulaciones típicas de la teoría de tipos. El antipatrón es una consecuencia simple del hecho de que es difícil trabajar con pilas profundas. Pueden ser algo más fáciles si implementa todas las operaciones de pila Forth para sus mónadas (o el conjunto estándar de transformadores Monad en Haskell).

El próximo mes voy a trabajar en un nuevo proyecto de I + D que adoptará un lenguaje de programación funcional (voté por Haskell, pero ahora F # obtuvo más consenso). Ahora, he jugado con esos idiomas por un tiempo y he desarrollado algunas herramientas de línea de comandos con ellos, pero este es un proyecto bastante más grande y estoy tratando de mejorar mi conocimiento y técnica de programación funcional. También he leído mucho sobre el tema, pero no puedo encontrar ningún libro o recurso que documente los patrones en el mundo de la programación funcional.

Ahora, aprender acerca de los anti-patrones significa aprender sobre los fracasos de otras personas inteligentes : en OOP, conozco algunos de ellos, y tengo la experiencia suficiente para elegir sabiamente cuando algo que generalmente es un anti-patrón, se ajusta perfectamente a mis necesidades. Pero puedo elegir esto porque conozco la lección aprendida por otros tipos inteligentes.

Por lo tanto, mi pregunta es: ¿hay algún anti-patterns documentado en la programación funcional? Hasta ahora, todos mis colegas me dijeron que no saben nada, pero no pueden decir por qué.

  • En caso afirmativo, incluya un solo enlace a una fuente autorizada (un catálogo, un ensayo, un libro o equivalente).
  • Si no, por favor apoya tu respuesta con un teorema adecuado .

Por favor , no conviertas esta pregunta en una lista : es una pregunta booleana que solo requiere una prueba para evaluar la respuesta. Por ejemplo, si eres Oleg Kiselyov, "Sí" es suficiente, ya que todos podrán encontrar tu ensayo sobre el tema. Aún así, por favor sea generoso.

Tenga en cuenta que estoy buscando antipatrones formales, no simples malos hábitos o malas prácticas.

Del anti-patterns :

... debe haber al menos dos elementos clave presentes para distinguir formalmente un antipatrón real de un simple mal hábito, mala práctica o mala idea :

  1. algún patrón repetido de acción, proceso o estructura que inicialmente parece ser beneficioso, pero en última instancia produce más consecuencias negativas que resultados beneficiosos, y
  2. Existe una solución alternativa que está claramente documentada, probada en la práctica real y repetible.

Además, por "documentado" quiero decir algo de autores autorizados o fuentes bien conocidas .

Los idiomas a los que estoy acostumbrado son:

  • Haskell (donde realmente estoy empezando a pensar que si el código se compila, ¡funciona!)
  • Scala
  • F#

pero también puedo adaptar el conocimiento acerca de los anti-patrones documentados en otros lenguajes funcionales.

Busqué mucho en la web, pero todos los recursos que he encontrado están relacionados con la POO o con el diseño de la función (definir variable al principio de la función, y similares).