user-interface haskell f# functional-programming

user interface - ¿Es posible la programación de GUI funcional?



user-interface haskell (15)

El enfoque de Haskell parece ser simplemente envolver los imperativos kits de herramientas de GUI (como GTK + o wxWidgets) y usar bloques "do" para simular un estilo imperativo

Ese no es realmente el "enfoque de Haskell", así es como se vincula de manera más directa con los imperativos juegos de herramientas GUI, a través de una interfaz imperativa. Haskell simplemente tiene enlaces bastante prominentes.

Existen varios enfoques moderadamente maduros, o más experimentales puramente funcionales / declarativos para las GUI, principalmente en Haskell, y que utilizan principalmente la programación reactiva funcional.

Algunos ejemplos son:

Para aquellos de ustedes que no están familiarizados con Haskell, Flapjax, http://www.flapjax-lang.org/ es una implementación de programación reactiva funcional sobre JavaScript.

Recientemente he detectado el error de FP (tratando de aprender Haskell), y me ha impresionado mucho lo que he visto hasta ahora (funciones de primera clase, evaluación perezosa y todos los demás beneficios). No soy un experto todavía, pero ya he empezado a encontrar que es más fácil razonar "funcionalmente" que imperativamente para los algoritmos básicos (y tengo problemas para volver a donde tengo que hacerlo).

Sin embargo, la única área en la que la FP actual parece ser plana es la programación GUI. El enfoque de Haskell parece ser simplemente envolver los imperativos kits de herramientas de GUI (como GTK + o wxWidgets) y usar bloques "do" para simular un estilo imperativo. No he usado F #, pero tengo entendido que hace algo similar al usar OOP con clases .NET. Obviamente, hay una buena razón para esto: la programación actual de GUI se trata de IO y efectos secundarios, por lo que la programación puramente funcional no es posible con la mayoría de los marcos actuales.

Mi pregunta es, ¿es posible tener un enfoque funcional para la programación GUI? Me cuesta imaginar cómo se vería esto en la práctica. ¿Alguien sabe de algún marco, experimental o no, que pruebe este tipo de cosas (o incluso cualquier marco que esté diseñado desde cero para un lenguaje funcional)? ¿O es la solución simplemente usar un enfoque híbrido, con OOP para las partes de la GUI y FP para la lógica? (Solo lo pregunto por curiosidad: me encantaría pensar que FP es "el futuro", pero la programación de GUI parece un agujero bastante grande de llenar).


Mi pregunta es, ¿es posible tener un enfoque funcional para la programación GUI?

Las palabras clave que busca son "programación reactiva funcional" (FRP).

Conal Elliott y algunos otros han hecho un poco de una industria casera al tratar de encontrar la abstracción correcta para FRP. Hay varias implementaciones de conceptos de FRP en Haskell.

Podría considerar comenzar con el documento más reciente de "Programación reactiva funcional de Push-Pull" de Conal, pero hay varias otras implementaciones (más antiguas), algunas vinculadas desde el sitio haskell.org . Conal tiene una habilidad especial para cubrir todo el dominio, y su documento se puede leer sin hacer referencia a lo que vino antes.

Para tener una idea de cómo se puede utilizar este enfoque para el desarrollo de GUI, es posible que desee ver Fudgets , que aunque en la Fudgets se está Fudgets un poco, al diseñarse a mediados de los años 90, presenta un enfoque sólido de FRP al diseño de GUI.


A partir de 2016, hay varios marcos FRP relativamente maduros para Haskell, como Sodium y Reflex (pero también Netwire).

El libro Manning sobre Programación reactiva funcional muestra la versión Java de Sodium, para ejemplos prácticos, e ilustra cómo se comporta y se amplía una base de código GUI de FRP en comparación con los enfoques imperativos y basados ​​en actores.

También hay un documento reciente sobre FRP con flecha y la posibilidad de incorporar efectos secundarios, IO y mutación en una configuración de FRP pura y respetuosa con la ley: haskell.cs.yale.edu/wp-content/uploads/2015/10/… .

También vale la pena señalar que los marcos de JavaScript como ReactJS y Angular y muchos otros ya están o están avanzando hacia el uso de un FRP o un enfoque funcional para lograr componentes de GUI escalables y compactables.


Desde que se hizo esta pregunta por primera vez, la programación reactiva funcional se ha hecho un poco más generalizada por Elm.

Sugiero visitarlo en http://elm-lang.org , que también tiene algunos tutoriales interactivos realmente excelentes sobre cómo crear una GUI en el navegador totalmente funcional.

Le permite crear GUI completamente funcionales donde el código que necesita para suministrarse consiste solo en funciones puras. Personalmente, me resultó mucho más fácil acceder a los distintos marcos de la GUI de Haskell.


La charla de Elliot sobre FRP se puede encontrar here .

Además, no es realmente una respuesta, sino un comentario y algunos pensamientos : de alguna manera el término "GUI funcional" parece un poco como un oxímoron (pureza e IO en el mismo término).

Pero mi vago entendimiento es que la programación funcional de la GUI se trata de definir de forma declarativa una función dependiente del tiempo que toma la entrada del usuario (real) dependiente del tiempo y produce una salida de la GUI dependiente del tiempo.

En otras palabras, esta función se define como una ecuación diferencial declarativa, en lugar de por un algoritmo que utiliza imperativamente el estado mutable.

Por lo tanto, en FP convencional, se usan funciones independientes del tiempo, mientras que en FRP se usan funciones dependientes del tiempo como bloques de construcción para describir un programa.

Pensemos en simular una bola en un resorte con el que el usuario pueda interactuar. La posición de la bola es la salida gráfica (en la pantalla), el usuario que empuja la bola es una pulsación de tecla (entrada).

La descripción de este programa de simulación en FRP (según mi entendimiento) se realiza mediante una única ecuación diferencial (declarativamente): aceleración * masa = - estiramiento de resorte * constante de resorte + Fuerza ejercida por el usuario.

Aquí hay un video en ELM que ilustra este punto de vista.


La innovación más evidente que notan las personas nuevas en Haskell es que existe una separación entre el mundo impuro que se preocupa por la comunicación con el mundo exterior y el mundo puro de computación y algoritmos. Una pregunta frecuente para principiantes es "¿Cómo puedo deshacerme de IO , es decir, convertir IO a en A?" La forma de hacerlo es usar mónadas (u otras abstracciones) para escribir código que realice efectos de E / S y cadenas. Este código recopila datos del mundo exterior, crea un modelo del mismo, realiza algunos cálculos, posiblemente empleando código puro, y genera el resultado.

En lo que respecta al modelo anterior, no veo nada terriblemente incorrecto en la manipulación de las GUI en la mónada IO . El mayor problema que surge de este estilo es que los módulos ya no se pueden componer, es decir, pierdo la mayor parte de mi conocimiento sobre el orden de ejecución global de las declaraciones en mi programa. Para recuperarlo, tengo que aplicar un razonamiento similar al de un código de GUI imperativo concurrente. Mientras tanto, para el código impuro, sin GUI, el orden de ejecución es obvio debido a la definición del operador >== de la mónada IO (al menos mientras exista un solo hilo). Para código puro, no importa en absoluto, excepto en casos de esquina para aumentar el rendimiento o para evitar evaluaciones que resulten en .

La mayor diferencia filosófica entre la consola y la IO gráfica es que los programas que implementan la primera generalmente se escriben en un estilo sincrónico. Esto es posible porque hay (dejando a un lado las señales y otros descriptores de archivos abiertos) solo una fuente de eventos: el flujo de bytes comúnmente llamado stdin . Sin embargo, las GUI son inherentemente asíncronas y tienen que reaccionar ante los eventos del teclado y los clics del mouse.

Una filosofía popular de hacer IO asíncrona de una manera funcional se llama Programación Reactiva Funcional (FRP). Recibió mucha tracción recientemente en lenguajes impuros y no funcionales gracias a bibliotecas como ReactiveX y marcos como Elm. En pocas palabras, es como ver elementos de la GUI y otras cosas (como archivos, relojes, alarmas, teclado, mouse) como fuentes de eventos, llamados "observables", que emiten flujos de eventos. Estos eventos se combinan utilizando operadores conocidos como map , foldl , zip , filter , concat , join , etc., para producir nuevas transmisiones. Esto es útil porque el estado del programa en sí puede verse como scanl . map reactToEvents $ zipN <eventStreams> scanl . map reactToEvents $ zipN <eventStreams> del programa, donde N es igual al número de observables considerados por el programa.

Trabajar con observables de FRP permite recuperar la capacidad de composición porque los eventos en una secuencia se ordenan a tiempo. La razón es que la abstracción del flujo de eventos hace posible ver todos los observables como cajas negras. En última instancia, la combinación de secuencias de eventos utilizando operadores devuelve algunos pedidos locales en la ejecución. Esto me obliga a ser mucho más honesto acerca de los invariantes en los que realmente se basa mi programa, de manera similar a como todas las funciones en Haskell tienen que ser referencialmente transparentes: si quiero extraer datos de otra parte de mi programa, tengo que ser explícito Anuncio declare un tipo apropiado para mis funciones. (La mónada IO, al ser un lenguaje específico del dominio para escribir código impuro, efectivamente evita esto)


La programación funcional puede haber pasado de cuando estaba en la universidad, pero como recuerdo, el punto principal de un sistema de programación funcional era detener al programador creando cualquier "efecto secundario". Sin embargo, los usuarios compran software debido a los efectos secundarios que se crean, por ejemplo, la actualización de una interfaz de usuario.


Los lenguajes de marcas como XUL le permiten construir una GUI de manera declarativa.


Para abordar esto, publiqué algunos pensamientos míos al usar F #,

http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/ http://fadsworld.wordpress.com/2011/04/17/fin-the-enterprise-ii- 2 /

También estoy planeando hacer un video tutorial para terminar la serie y mostrar cómo F # puede contribuir en la programación de UX.

Sólo estoy hablando en el contexto de F # aquí.

-Fahad



Realmente diría que la programación funcional (F #) es una herramienta mucho mejor para la programación de la interfaz de usuario que, por ejemplo, C #. Solo necesitas pensar el problema de manera un poco diferente.

Discuto este tema en mi libro de programación funcional en el Capítulo 16, pero hay un extracto gratuito disponible , que muestra (IMHO) el patrón más interesante que puede usar en F #. Digamos que desea implementar el dibujo de rectángulos (el usuario presiona el botón, mueve el mouse y suelta el botón). En F #, puedes escribir algo como esto:

let rec drawingLoop(clr, from) = async { // Wait for the first MouseMove occurrence let! move = Async.AwaitObservable(form.MouseMove) if (move.Button &&& MouseButtons.Left) = MouseButtons.Left then // Refresh the window & continue looping drawRectangle(clr, from, (move.X, move.Y)) return! drawingLoop(clr, from) else // Return the end position of rectangle return (move.X, move.Y) } let waitingLoop() = async { while true do // Wait until the user starts drawing next rectangle let! down = Async.AwaitObservable(form.MouseDown) let downPos = (down.X, down.Y) if (down.Button &&& MouseButtons.Left) = MouseButtons.Left then // Wait for the end point of the rectangle let! upPos = drawingLoop(Color.IndianRed, downPos) do printfn "Drawn rectangle (%A, %A)" downPos upPos }

Este es un enfoque muy imperativo (en el estilo pragmático habitual de F #), pero evita el uso de un estado mutable para almacenar el estado actual del dibujo y para almacenar la ubicación inicial. Aunque se puede hacer aún más funcional, escribí una biblioteca que lo hace como parte de mi tesis de maestría, que debería estar disponible en mi blog en los próximos días.

La programación reactiva funcional es un enfoque más funcional, pero me resulta un poco más difícil de usar, ya que se basa en características de Haskell bastante avanzadas (como las flechas). Sin embargo, es muy elegante en un gran número de casos. Su limitación es que no puede codificar fácilmente una máquina de estados (que es un modelo mental útil para programas reactivos). Esto es muy fácil usando la técnica F # arriba.


Todas estas otras respuestas se basan en la programación funcional, pero toman muchas de sus propias decisiones de diseño. Una biblioteca que está construida básicamente de funciones y tipos de datos abstractos simples es gloss . Aquí está el tipo para su función de play de la fuente

-- | Play a game in a window. Like `simulate`, but you manage your own input events. play :: Display -- ^ Display mode. -> Color -- ^ Background color. -> Int -- ^ Number of simulation steps to take for each second of real time. -> world -- ^ The initial world. -> (world -> Picture) -- ^ A function to convert the world a picture. -> (Event -> world -> world) -- ^ A function to handle input events. -> (Float -> world -> world) -- ^ A function to step the world one iteration. -- It is passed the period of time (in seconds) needing to be advanced. -> IO ()

Como puede ver, funciona completamente al proporcionar funciones puras con tipos abstractos simples, con las que otras bibliotecas le ayudan.


Una de las ideas que abren la mente detrás de la Programación reactiva funcional es tener una función de manejo de eventos que produzca AMBAS reacciones a los eventos Y la siguiente función de manejo de eventos. Por lo tanto, un sistema en evolución se representa como una secuencia de funciones de manejo de eventos.

Para mí, el aprendizaje de Yampa se convirtió en un punto crucial para lograr que las funciones produzcan funciones correctamente. Hay algunos buenos papeles sobre Yampa. Recomiendo The Yampa Arcade:

http://www.cs.nott.ac.uk/~nhn/Talks/HW2003-YampaArcade.pdf (diapositivas, PDF) http://www.cs.nott.ac.uk/~nhn/Publications/hw2003.pdf (artículo completo, PDF)

Hay una página wiki en Yampa en Haskell.org

http://www.haskell.org/haskellwiki/Yampa

Página de inicio original de Yampa:

http://www.haskell.org/yampa (desafortunadamente está roto en este momento)


Ya sea que esté en un lenguaje híbrido funcional / OO como F # u OCaml, o en un lenguaje puramente funcional como Haskell, donde los efectos secundarios están relegados a la mónada IO, es principalmente el caso que una tonelada del trabajo requerido para administrar una GUI es mucho más como un "efecto secundario" que como un algoritmo puramente funcional.

Dicho esto, se ha realizado una investigación realmente sólida en las GUI funcionales . Incluso hay algunos (en su mayoría) kits de herramientas funcionales como Fudgets o FranTk .


Windows Presentation Foundation es una prueba de que el enfoque funcional funciona muy bien para la programación GUI. Tiene muchos aspectos funcionales y el código "bueno" de WPF (búsqueda del patrón MVVM) enfatiza el enfoque funcional sobre el imperativo. Podría afirmar valientemente que WPF es el conjunto de herramientas de GUI funcional más exitoso del mundo real :-)

WPF describe la interfaz de usuario en XAML (aunque también puede reescribirla para buscar funcionalmente C # o F #), así que para crear alguna interfaz de usuario escribiría:

<!-- Declarative user interface in WPF and XAML --> <Canvas Background="Black"> <Ellipse x:Name="greenEllipse" Width="75" Height="75" Canvas.Left="0" Canvas.Top="0" Fill="LightGreen" /> </Canvas>

Además, WPF también le permite describir animativamente las reacciones y reacciones a eventos usando otro conjunto de etiquetas declarativas (una vez más, lo mismo puede escribirse como código C # / F #):

<DoubleAnimation Storyboard.TargetName="greenEllipse" Storyboard.TargetProperty="(Canvas.Left)" From="0.0" To="100.0" Duration="0:0:5" />

De hecho, creo que WPF tiene muchas cosas en común con el FRP de Haskell (aunque creo que los diseñadores de WPF no sabían sobre el FRP y es un poco desafortunado: a veces WPF se siente un poco raro y poco claro si está usando el funcional punto de vista).