clojure model javafx cad

Clojure: cómo diseñar la interfaz de usuario de escritorio



model javafx (1)

Estoy tratando de diseñar una interfaz de usuario de escritorio para esquemas, diseño, material de dibujo. Solo estoy buscando consejos de alto nivel de diseñadores de software reales.

Suponiendo una "base de datos" en memoria, (mapa clojure de profundidad arbitraria para todos los datos del usuario, y posiblemente otro para las preferencias de la aplicación, etc.), estoy examinando cómo hacer lo de la vista modelo-controlador en estos, donde los datos pueden ser renderizados y modificados por uno o más de:

  1. Un campo de texto independiente que muestra un solo parámetro, como el ancho del cuadro.
  2. Un tipo de vista de "inspector" que muestra múltiples parámetros de un objeto seleccionado, como el ancho, la altura, el color, las casillas de verificación, etc.
  3. Un tipo de vista de tabla / hoja de cálculo que muestra múltiples parámetros de múltiples objetos, potencialmente toda la base de datos
  4. Una representación gráfica de todo el conjunto, como la vista esquemática y la vista de diseño.

La modificación de cualquiera de estos debe aparecer inmediatamente en cualquier otra vista activa, tanto de texto como gráfica, no después de hacer clic en "ok" ... por lo que no se permiten recuadros modales. Si por alguna razón la vista de tabla, una vista de inspector y una representación gráfica están a la vista, al arrastrar la esquina del cuadro debe aparecer inmediatamente en el texto, etc.

La plataforma en cuestión es JavaFX, pero me gustaría una separación limpia entre UI y todo lo demás, así que quiero evitar bind en el sentido JFX, ya que eso vincula mis datos de diseño muy estrechamente con las Propiedades JFX, aumenta la granulosidad de la modelo, y me obliga a trabajar fuera de las funciones de clojure estándar para tratar con los datos, y / o manejar en gran medida todo el mundo getValue / setValue .

Todavía asumo al menos cierto estado / mutabilidad, y el uso de la funcionalidad integrada Clojure, como la capacidad de add-watch en un átomo / var / ref y dejar que la señal de tiempo de ejecución dependa de las funciones.

La interacción específica de la plataforma descansará estrechamente con la interfaz de usuario real, como la reificación de ActionListener , y el tratamiento de los valores de ObservableValue , etc., y tratará de minimizar la dependencia de elementos como la Property JavaFX para los datos reales de la aplicación. No estoy entreteniendo a FRP por esto.

No me importa ampliar las interfaces JFX o crear mis propios protocolos para usar defrecord específicos de la defrecord , pero preferiría que los datos de la aplicación permanezcan como datos correctos de Clojure, sin que la plataforma los inunde.

La pregunta es cómo configurar todo esto, con la más estrecha adhesión al modelo inmutable. Veo algunas opciones:

  1. Fino: cada valor de parámetro / primitiva (es decir, largo, doble, booleano o cadena) es un átomo, y cada vista que puede modificar el valor "alcanza" todo lo necesario en la base de datos para cambiar el valor. Esto podría ser una mierda, ya que podría haber miles de valores individuales (por ejemplo, puntos en una curva dibujada a mano), y requerirá mucha (deref...) . Creo que así es como JFX querría hacer esto, con matrices gigantes de Propiedades en los nodos de las hojas, etc., que se sienten hinchadas. Con este enfoque, no parece mucho mejor que simplemente codificarlo en Java / C ++.
  2. Medium-grain: cada objeto / registro en la base de datos es un átomo de un mapa de Clojure. El mapa completo se reemplaza cuando cambia cualquiera de sus valores. Menos átomos totales para tratar, y permite, por ejemplo, largos arreglos de números rectos para varias cosas. Pero esto se complica cuando algunos objetos en la base de datos requieren más anidamiento que otros.
  3. Grano grueso: solo hay un átomo: la base de datos. Cada vez que algo cambia, se reemplaza toda la base de datos y cada vista debe volver a mostrar su porción particular. Esto se siente como usar un martillo para golpear una mosca, y una implementación ingenua requeriría que todo vuelva a reproducirse todo el tiempo. Pero sigo pensando que esta es la mejor solución, ya que cualquier primitiva tiene una ruta de acceso clara desde el nodo raíz, ya sea que se acceda a nivel primitivo o por registro.

También necesito la capacidad de crear una instancia de una plantilla de datos muchas veces. Entonces, por ejemplo, si el usuario cambia un símbolo o forma que se usa en varios lugares, una sola edición se aplicará en todas partes. Creo que esto también requiere algún tipo de comportamiento tipo "puntero". Creo que puedo almacenar un átomo en el modelo, luego crear una instancia según sea necesario y puede funcionar en cualquiera de los modelos de grano anteriores.

Cualquier otro enfoque? Está tratando de hacer una herramienta tipo editor GUI en un lenguaje funcional simplemente estúpido? Gracias


No creo que sea estúpido usar un lenguaje funcional para hacer una herramienta tipo editor de GUI. Pero no puedo afirmar tener una respuesta a su pregunta. Aquí hay algunos enlaces que pueden ayudarlo en su viaje:

  1. Stuart Sierra - Componentes Just Enough Structure
  2. Chris Granger - Light Table : explica cómo se estructura Light Table ( fuente ).
  3. Chris Granger - El IDE como valor : entrada de blog relacionada con el video de arriba
  4. Conal Elliott: programación funcional tangible : usa la programación reactiva funcional para crear una interfaz de usuario composable, pero su código está en Haskell.
  5. Nathan Herzing y Chris Shea - Ayudando a los votantes con Pedestal, Datomic, Om y core.async
  6. David Nolen - Programación Literaria Comparada : muestra a todos para usar core.async para simplificar la programación de IU en ClojureScript. Las ideas aquí se pueden usar en una interfaz de usuario de escritorio.
  7. Rich Hickey - El lenguaje del sistema : sorprendente charla sobre la programación del sistema por el creador de Clojure.

Erik Meijer tiene una buena cita sobre el código funcional versus el imperativo:

... sin importar si Haskell, C # Java, F #, Scala, Python, PHP piensan en la idea de tener un mar de código imperativo que interactúa con el mundo exterior y allí tienen islas de código puro donde escribes tus funciones en una manera pura. Pero debes decidir qué tan grandes son las islas y qué tan grande es el mar. Pero la respuesta nunca es que solo haya islas o solo mar. Un buen programador sabe exactamente el equilibrio correcto.