programming languages functional example course books design functional-programming paradigms

design - languages - Luchando con el uso de la programación funcional pura para resolver un problema cotidiano



functional programming python (3)

La única forma de cambiar tu forma de pensar es cambiar tu forma de pensar. Puedo decirte lo que funcionó para mí:

Quería trabajar en un proyecto personal que requería concurrencia. Miré a mi alrededor y encontré erlang. Lo elegí porque pensé que tenía el mejor soporte para la concurrencia, no por ninguna otra razón. Nunca antes había trabajado con un lenguaje funcional (y solo para comparar, comencé a hacer programación orientada a objetos a principios de la década de 1990).

Leí el libro de erlang de Armstrong. Fue difícil. Tenía un pequeño proyecto para trabajar, pero seguí insistiendo.

El proyecto fue un fracaso, pero después de un par de meses había mapeado todo en mi cabeza lo suficiente como para no pensar en los objetos de la forma en que solía hacerlo.

Pasé por una fase en la que asigné objetos a los procesos de erlang, pero no fue demasiado largo y salí de allí.

Ahora cambiar paradigmas es como cambiar de idioma o ir de un automóvil a otro. Conducir el automóvil de mi padre se siente diferente de mi camión, pero no lleva mucho tiempo acostumbrarse a él.

Creo que trabajar en Python podría estar reteniéndote, y te recomiendo encarecidamente que revises erlang. Su sintaxis es muy extraña, pero eso también es bueno, ya que una sintaxis más tradicional (al menos para mí) llevaría a intentar programarla de la manera anterior y frustrarla.

Vi esta publicación en las noticias de los hackers de hoy. Estoy luchando con los mismos problemas para comprender cómo la programación funcional pura me ayudará a abstraer un problema del mundo real. Hice el cambio de imperativo a la programación OO hace 7 años. Siento que lo he dominado, y me ha servido bien. En los últimos años he aprendido algunos trucos y conceptos en programación funcional como map y reduce, y también me gustan. Los he usado en mi código OO y estoy contento con eso, pero al resumir un conjunto de instrucciones, solo puedo pensar en abstracciones OO para hacer que el código sea más bonito.

Recientemente he estado trabajando en un problema en Python, y he estado tratando de evitar el uso de OO para resolverlo. En su mayor parte, mi solución parece imprescindible, y sé que podría hacer que se vea bien y limpia si utilizo OO. Pensé que publicaría el problema, y ​​tal vez los expertos funcionales pueden plantear una solución que es hermosa y funcional. Puedo publicar mi código feo si debo hacerlo, pero preferiría no hacerlo. :) Aquí está el problema:

El usuario puede solicitar una imagen o una miniatura de la imagen. Si el usuario solicita la miniatura de la imagen y aún no existe, créela utilizando el módulo PIL de python. Cree también un enlace simbólico al original o miniatura con una ruta legible para el ser humano, porque el nombre original de la imagen es un código hash, y no descriptivo de su contenido. Finalmente, redirija al enlace simbólico de esa imagen.

En OO probablemente crearía una clase base SymlinkImage, una subclase ThumbnailSymlinkImage y una subclase OriginalSymlinkImage. Los datos compartidos (en la clase SymlinkImage) serán cosas como la ruta al original. El comportamiento compartido creará el enlace simbólico. Las subclases implementarán un método llamado algo así como ''generar'' que será responsable de crear la miniatura, si corresponde, y realizar la llamada a su superclase para crear el nuevo enlace simbólico.


Personalmente, creo que el problema es que estás tratando de usar la Programación Funcional para resolver problemas que están diseñados / establecidos para la programación de Imperativo. Los 3 paradigmas populares (funcional, imperativo, orientado a objetos) tienen diferentes puntos fuertes:

  • La Programación funcional enfatiza en la descripción de QUÉ hacer, generalmente en términos de entrada / resultado.
  • La Programación Imperativa enfatiza en CÓMO hacer algo, generalmente en términos de la lista y el orden de los pasos a seguir, y los estados para modificar.
  • La Programación orientada a objetos enfatiza las RELACIONES entre entidades en un sistema

Por lo tanto, cuando aborda un problema, lo primero que debe hacer es reformularlo de modo que el paradigma deseado pueda resolverlo adecuadamente. Por cierto, como nodo lateral, no existe tal cosa como "OOP puro" hasta donde yo sé. El código en los métodos de las clases de OOP (ya sea Java, C #, C ++, Python o Objective C) es todo imperativo.

Volviendo a su ejemplo: la forma en que usted declara su problema (primero, luego, también, finalmente) es imperativo por naturaleza. Como tal, la construcción de una solución funcional es casi imposible (sin hacer los trucos como efecto secundario o mónadas, eso es). Del mismo modo, incluso si crea un grupo de clases, esas clases son inútiles en sí mismas. Para usarlos, debe escribir el código imperativo (aunque estos códigos estén integrados en las clases) que resuelven el problema paso a paso.

Para replantear el problema:

  • Entrada: tipo de imagen (completa o miniatura), nombre de la imagen, sistema de archivos
  • Salida: la imagen solicitada, el sistema de archivos con la imagen solicitada

Del nuevo enunciado del problema, puedes resolverlo así:

def requestImage(type, name, fs) : if type == "full" : return lookupImage(name, fs), fs else: thumb = lookupThumb(name, fs) if(thumb) : return thumb, fs else: thumb = createThumbnail(lookupImage(name, fs)) return thumb, addThumbnailToFs(fs, name, thumb)

Por supuesto, esto es incompleto, pero siempre podemos resolver recurrentemente lookupImage, lookupThumb, createThumbnail, y addThumbnailToFs aproximadamente de la misma manera.

Nota importante: la creación de un nuevo sistema de archivos suena a gran escala, pero no debería ser así. Por ejemplo, si se trata de un módulo en un servidor web más grande, el "nuevo sistema de archivos" puede ser tan simple como las instrucciones sobre dónde debe estar la nueva miniatura. O, en el peor de los casos, puede ser una mónada IO colocar la miniatura en la ubicación adecuada.


Sí, harías esto de manera muy diferente usando un enfoque funcional.

Aquí hay un boceto que usa el lenguaje de programación escrito, por defecto, puro y funcional Haskell . Creamos nuevos tipos para los conceptos clave de su problema y separamos el trabajo en funciones discretas que realizan una tarea a la vez. El IO y otros efectos secundarios (como la creación de un enlace simbólico) están restringidos a ciertas funciones, y se indican con un tipo. Para distinguir los dos modos de operación, utilizamos un tipo de suma .

-- -- User can request an image or a thumbnail of the image. -- If the user requests the thumbnail of the image, and it doesn''t yet exist, create it using -- python''s PIL module. Also create a symbolic link to the original or -- thumbnail with a human readable path, because the original image name is a -- hashcode, and not descriptive of it''s contents. Finally, redirect to the -- symbolic link of that image. -- module ImageEvent where import System.FilePath import System.Posix.Files -- Request types data ImgRequest = Thumb ImgName | Full ImgName -- Hash of image type ImgName = String -- Type of redirects data Redirect request :: ImgRequest -> IO Redirect request (Thumb img) = do f <- createThumbnail img let f'' = normalizePath f createSymbolicLink f f'' return (urlOf f) request (Full img) = do createSymbolicLink f f'' return (urlOf f) where f = lookupPath img f'' = normalizePath f

Junto con algunos ayudantes, que le dejaré la definición a usted.

-- Creates a thumbnail for a given image at a path, returns new filepath createThumbnail :: ImgName -> IO FilePath createThumbnail f = undefined where p = lookupPath f -- Create absolute path from image hash lookupPath :: ImgName -> FilePath lookupPath f = "/path/to/img" </> f <.> "png" -- Given an image, construct a redirect to that image url urlOf :: FilePath -> Redirect urlOf = undefined -- Compute human-readable path from has normalizePath :: FilePath -> FilePath normalizePath = undefined

Una solución verdaderamente hermosa podría abstraer el modelo de solicitud / respuesta con una estructura de datos para representar la secuencia de comandos que se ejecutarán. Se produce una solicitud, crea una estructura puramente para representar el trabajo que necesita y se entrega al motor de ejecución con cosas como crear archivos, etc. Entonces la lógica central será funciones completamente puras (no es que haya mucha lógica central en este problema). Para un ejemplo de este estilo de programación verdaderamente puramente funcional con efectos, recomiendo el artículo de Wouter Swiestra, `` Belleza en la bestia: una semántica funcional para el escuadrón torpe ''''.