una tipos practicos organizacional laboral empresa elementos ejemplos ejemplo comportamientos comportamiento cola coca caracteristicas haskell reactive-programming frp

haskell - tipos - elementos del comportamiento organizacional



Dónde aplicar el comportamiento(y otros tipos) en FRP (2)

Estoy de acuerdo con el consejo de dflemstr

  1. Aislar lo "que cambia" tanto como sea posible.
  2. Agrupe "cosas que cambian simultáneamente" en un Behavior/Event .

y quisiera ofrecer razones adicionales para estas reglas de oro.

La pregunta se reduce a lo siguiente: desea representar un par (tupla) de valores que cambian en el tiempo y la pregunta es si usar

a. (Behavior x, Behavior y) - un par de comportamientos

segundo. Behavior (x,y) - un comportamiento de pares

Las razones para preferir una sobre la otra son

  • a sobre b .

    En una implementación impulsada por empuje, el cambio de un comportamiento activará un recálculo de todos los comportamientos que dependen de él.

    Ahora, considere un comportamiento cuyo valor depende solo del primer componente x del par. En la variante a , un cambio del segundo componente y no volverá a calcular el comportamiento. Pero en la variante b , el comportamiento se recalculará, aunque su valor no dependa en absoluto del segundo componente. En otras palabras, se trata de dependencias de grano fino frente a dependencias de grano grueso.

    Este es un argumento para el consejo 1. Por supuesto, esto no es de mucha importancia cuando ambos comportamientos tienden a cambiar simultáneamente, lo que da como resultado el consejo 2.

    Por supuesto, la biblioteca debería ofrecer una forma de ofrecer dependencias detalladas incluso para la variante b . A partir de la versión 0.4.3 de banana reactiva, esto no es posible, pero no se preocupe por eso por ahora, mi implementación impulsada va a madurar en versiones futuras.

  • b sobre a .

    Al ver que la versión 0.4.3 de reactiva-banana aún no ofrece cambio dinámico de eventos , hay ciertos programas que solo puede escribir si coloca todos los componentes en un solo comportamiento. El ejemplo canoncial sería un programa que presenta un número variable de contadores, es decir, una extensión del ejemplo de TwoCounter.hs . Tienes que representarlo como una lista de valores que cambia el tiempo.

    counters :: Behavior [Int]

    porque no hay manera de seguir la pista de una colección dinámica de comportamientos todavía. Dicho esto, la próxima versión de reactive-banana incluirá el cambio dinámico de eventos.

    Además, siempre puede convertir de la variante a la variante b sin ningún problema

    uncurry (liftA2 (,)) :: (Behavior a, Behavior b) -> Behavior (a,b)

Estoy trabajando en un programa que utiliza reactive-banana , y me pregunto cómo estructurar mis tipos con los bloques de construcción básicos de FRP.

Por ejemplo, aquí hay un ejemplo simplificado de mi programa real: digamos que mi sistema está compuesto principalmente de widgets , en mi programa, fragmentos de texto que varían con el tiempo.

podría tener

newtype Widget = Widget { widgetText :: Behavior String }

pero también podría tener

newtype Widget = Widget { widgetText :: String }

y usar el Behavior Widget cuando quiero hablar sobre el comportamiento que varía con el tiempo. Esto parece hacer las cosas "más simples", y significa que puedo usar las operaciones de Behavior más directamente, en lugar de tener que desempaquetar y volver a empaquetar los Widgets para hacerlo.

Por otro lado, el primero parece evitar la duplicación en el código que realmente define los widgets, ya que casi todos los widgets varían con el tiempo, y me encuentro definiendo incluso los pocos que no lo hacen con Behavior , ya que me permite combinarlos con los demás de una manera más consistente.

Como otro ejemplo, con ambas representaciones, tiene sentido tener una instancia de Monoid (y quiero tener una en mi programa), pero la implementación de esta última parece más natural (ya que es solo un levantamiento trivial de la lista de monoides a nuevo tipo).

(Mi programa real usa Discrete lugar de Behavior , pero no creo que sea relevante).

Del mismo modo, ¿debo usar Behavior (Coord,Coord) o (Behavior Coord, Behavior Coord) Para representar un punto 2D? En este caso, la primera parece ser la opción obvia; pero cuando se trata de un registro de cinco elementos que representa algo como una entidad en un juego, la elección parece menos clara.

En esencia, todos estos problemas se reducen a:

Al usar FRP, ¿en qué capa debo aplicar el tipo de Behavior ?

(La misma pregunta se aplica al Event también, aunque en menor grado.)


Las reglas que utilizo al desarrollar aplicaciones de FRP, son:

  1. Aislar lo "que cambia" tanto como sea posible.
  2. Agrupe "cosas que cambian simultáneamente" en un Behavior / Event .

La razón para (1) es que es más fácil crear y componer operaciones abstractas si los tipos de datos que utiliza son lo más primitivos posible.

La razón de esto es que instancias como Monoid se pueden reutilizar para tipos sin formato, como describió.

Tenga en cuenta que puede usar Lenses para modificar fácilmente los "contenidos" de un tipo de datos como si fueran valores en bruto, por lo que el "ajuste / desenvolvimiento" adicional no es un problema, en su mayoría. (Vea este tutorial reciente para una introducción a esta implementación de Lens en particular; hay others )

El motivo de (2) es que simplemente elimina los gastos generales innecesarios. Si dos cosas cambian simultáneamente, "tienen el mismo comportamiento", por lo que deberían modelarse como tales.

Ergo / tl; dr : Debería usar newtype Widget = Widget { widgetText :: Behavior String } debido a (1), y debería usar Behavior (Coord, Coord) debido a (2) (ya que ambas coordenadas generalmente cambian simultáneamente).