¿Es posible la progtwigción funcional de GUI?

Recientemente he detectado el error de FP (tratando de aprender Haskell), y he quedado realmente impresionado con lo que he visto hasta ahora (funciones de primera clase, evaluación perezosa y todas las demás cosas). Todavía no soy un experto, pero ya he comenzado a encontrar que es más fácil razonar “funcionalmente” que imperativamente para algoritmos básicos (y tengo problemas para volver donde tengo que hacerlo).

Sin embargo, la única área donde la FP actual parece no funcionar, es la progtwigción de GUI. El enfoque de Haskell parece ser simplemente envolver herramientas de GUI imperativas (como GTK + o wxWidgets) y usar bloques “do” para simular un estilo imperativo. No he usado F #, pero tengo entendido que hace algo similar usando OOP con clases .NET. Obviamente, hay una buena razón para esto: la progtwigción actual de GUI se trata de IO y efectos secundarios, por lo que la progtwigció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 progtwigción de GUI? Tengo problemas para imaginar cómo se vería esto en la práctica. ¿Alguien sabe de algún marco, experimental o de otro tipo, que pruebe este tipo de cosas (o incluso cualquier marco que esté diseñado desde cero para un lenguaje funcional)? ¿O la solución es simplemente utilizar un enfoque híbrido, con OOP para las partes de la GUI y FP para la lógica? (Solo pregunto por curiosidad, me encantaría pensar que FP es “el futuro”, pero la progtwigción de GUI parece un gran agujero para llenar).

El enfoque de Haskell parece ser simplemente envolver herramientas de GUI imperativas (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 une a los imperativos de los juegos de herramientas GUI más directamente – a través de una interfaz imperativa. Haskell tiene enlaces bastante prominentes.

Existen varias aproximaciones moderadamente maduras, o más experimentales puramente funcionales / declarativas a las GUI, principalmente en Haskell, y principalmente el uso de progtwigció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 progtwigción reactiva funcional además de JavaScript.

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

Las palabras clave que está buscando son “progtwigción reactiva funcional” (FRP).

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

Puede considerar comenzar con el documento más reciente de ” Conal Functional Reactive Programming” de Conal, pero hay varias otras implementaciones (más antiguas), algunas vinculadas desde el sitio de haskell.org . Conal tiene una habilidad especial para cubrir todo el dominio, y su documento puede leerse sin referencia a lo que vino antes.

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

Windows Presentation Foundation es una prueba de que el enfoque funcional funciona muy bien para la progtwigción de 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 con valentía que WPF es el juego de herramientas GUI funcional más exitoso del mundo real 🙂

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

    

Además, WPF también le permite describir declarativamente animaciones y reacciones a eventos utilizando otro conjunto de tags declarativas (nuevamente, lo mismo puede escribirse como código C # / F #):

  

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 acerca de FRP y es un poco desafortunado – WPF a veces se siente un poco raro y poco claro si estás usando el funcional punto de vista).

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

Discuto este tema en mi libro de progtwigción funcional en el Capítulo 16, pero hay un extracto libre disponible , que muestra (en mi humilde opinión) el patrón más interesante que puede usar en F #. Supongamos que desea implementar el dibujo de rectangularjs (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 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 hace eso como parte de mi tesis de maestría, que debería estar disponible en mi blog en los próximos días.

La Progtwigción Reactiva Funcional es un enfoque más funcional, pero me resulta un tanto más difícil de usar ya que se basa en funciones Haskell bastante avanzadas (como las flechas). Sin embargo, es muy elegante en una gran cantidad de casos. Su limitación es que no se puede codificar fácilmente una máquina de estados (que es un modelo mental útil para progtwigs reactivos). Esto es muy fácil usando la técnica F # anterior.

Ya sea que esté en un lenguaje funcional / OO híbrido como F # u OCaml, o en un lenguaje puramente funcional como Haskell donde los efectos secundarios se relegan a la mónada IO, es mayormente el caso de que una tonelada de trabajo requiera administrar una GUI es mucho más como un “efecto secundario” que como un algoritmo puramente funcional.

Dicho esto, ha habido algunas investigaciones realmente sólidas puestas en GUI funcionales . Incluso hay algunos (principalmente) juegos de herramientas funcionales como Fudgets o FranTk .

Puede ver la serie de Don Syme en F # donde la demo está creando una GUI. el siguiente enlace es para la tercera parte de la serie (puede vincular desde allí a las otras dos partes).

Usar F # para el desarrollo de WPF sería un paradigma de GUI muy interesante …

http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Don-Syme-Introduction-to-F-3-of-3/

Una de las ideas que abren la mente detrás de la Progtwigción Reactiva Funcional es tener una función de manejo de eventos que produzca AMBAS reacciones a los eventos Y la próxima 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 elemento crucial para lograr que las funciones-funciones-de-producción se llevaran a cabo correctamente. Hay algunos buenos documentos 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 (lamentablemente está roto en este momento)

El discurso de Elliot sobre FRP se puede encontrar aquí .

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

Pero mi vaga comprensión es que la progtwigción funcional de la GUI se trata de definir declarativamente una función dependiente del tiempo que toma la entrada del usuario dependiente del tiempo (real) y produce una salida GUI dependiente del tiempo.

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

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

Pensemos en simular una pelota en un muelle con el que el usuario puede 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 progtwig de simulación en FRP (según mi comprensión) se realiza mediante una única ecuación diferencial (declarativa): aceleración * masa = – estiramiento de la spring * constante de la spring + Fuerza ejercida por el usuario.

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

Desde que se hizo esta pregunta por primera vez, la progtwigción funcional reactiva se ha vuelto un poco más convencional por Elm.

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

Le permite hacer GUI completamente funcionales donde el código que necesita suministrarse solo consiste en funciones puras. Personalmente, me resultó mucho más fácil entrar que los diversos frameworks de GUI de Haskell.

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

El libro de Manning sobre Progtwigción Reactiva Funcional muestra la versión Java de Sodium, para ejemplos de trabajo, e ilustra cómo se comporta y se escala una base de código FRP GUI en comparación con enfoques imperativos y basados ​​en Actor.

También hay un documento reciente sobre FRP con flechas y la posibilidad de incorporar efectos secundarios, IO y mutaciones en un marco FRP puro y respetuoso de la ley: http://haskell.cs.yale.edu/wp-content/uploads/2015/10/ dwc-yale-formatted-dissertation.pdf .

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 u otro enfoque funcional para lograr componentes GUI escalables y compostables.

Los lenguajes de marcado 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 progtwigción de UX.

Solo estoy hablando en contexto de F # aquí.

-Fahad

Todas estas otras respuestas se basan en la progtwigción funcional, pero toman muchas de sus propias decisiones de diseño. Una biblioteca que está construida básicamente por completo de funciones y tipos de datos abstractos simples es gloss . Este es el tipo de 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 mediante el suministro de funciones puras con tipos abstractos simples, que otras bibliotecas le ayudan con.

La innovación más evidente que notaron las personas nuevas en Haskell es que existe una separación entre el mundo impuro que se ocupa de la comunicación con el mundo exterior y el mundo puro de la computación y los 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 IO y cadenas. Este código recostack datos del mundo exterior, crea un modelo de él, realiza algunos cálculos, posiblemente mediante el uso de código puro, y genera el resultado.

En lo que respecta al modelo anterior, no veo nada terriblemente mal con la manipulación de 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 progtwig. Para recuperarlo, tengo que aplicar un razonamiento similar al del código GUI imperativo simultáneo. Mientras tanto, para el código impuro, no GUI, el orden de ejecución es obvio debido a la definición del operador IO mónada >== (al menos mientras haya un solo hilo). Para código puro, no importa en absoluto, excepto en casos de esquina para boost el rendimiento o para evitar evaluaciones que resultado .

La mayor diferencia filosófica entre la consola y la IO gráfica es que los progtwigs que implementan la primera generalmente se escriben en estilo síncrono. Esto es posible porque hay (dejando de lado las señales y otros descriptores de archivos abiertos) una sola fuente de eventos: la secuencia de bytes comúnmente llamada stdin . Sin embargo, las GUI son inherentemente asíncronas y tienen que reactjsr a los eventos del teclado y a los clics del mouse.

Una filosofía popular de hacer IO asíncrona de una manera funcional se llama Progtwigción Funcional Reactiva (FRP). Recientemente obtuvo mucha tracción en lenguajes impuros y no funcionales gracias a bibliotecas como ReactiveX y frameworks como Elm. En pocas palabras, es como ver elementos de GUI y otras cosas (como archivos, relojes, alarmas, teclado, mouse) como fonts de eventos, llamadas “observables”, que emiten flujos de eventos. Estos eventos se combinan utilizando operadores familiares como map , foldl , zip , filter , concat , join , etc., para producir nuevas transmisiones. Esto es útil porque el estado del progtwig en sí se puede ver como scanl . map reactToEvents $ zipN scanl . map reactToEvents $ zipN del progtwig, donde N es igual al número de observables jamás considerados por el progtwig.

Trabajar con observables de FRP permite recuperar la capacidad de comstackció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 cuadros negros. 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 sobre los invariantes de los que mi progtwig realmente se basa, similar a la forma en que todas las funciones en Haskell tienen que ser referencialmente transparentes: si quiero extraer datos de otra parte de mi progtwig, tengo que ser explícito ad declara un tipo apropiado para mis funciones. (La mónada IO, al ser un lenguaje específico de dominio para escribir código impuro, lo elude de manera efectiva)

La progtwigción funcional puede haber cambiado desde cuando estaba en la universidad, pero según recuerdo, el punto principal de un sistema de progtwigción funcional era evitar que el progtwigdor creara algún “efecto secundario”. Sin embargo, los usuarios compran software debido a los efectos secundarios que se crean, por ejemplo, al actualizar una IU.