2012-07-31 12 views
7

Soy nuevo en banana reactiva y FRP en general, así que me disculpo si me falta algo obvio.Implementación de zipE :: Evento t a -> Evento t b -> Evento t (a, b)

Para my project (a GDB/MI front-end) Estoy usando reactive banana (versión 0.6.0.0) tanto para la interfaz gráfica de usuario como para los módulos lógicos frontales. El primero funciona muy bien, pero para este último aparentemente necesito otros combinadores.

Uno de ellos es zipE :: Event t a -> Event t b -> Event t (a, b). Por desgracia, todo lo que podía llegar a es una solución en la mónada NetworkDescription que utiliza changes y no es genérica en los tipos de eventos:

zipE :: Event t Int -> Event t String -> NetworkDescription t (Event t (Int, String)) 
zipE ea eb = changes $ (,) <$> stepper 0 ea <*> stepper "" eb 

Por supuesto, no estoy satisfecho con esto. Por lo tanto, quería preguntar cómo implementar una función zipE genérica sin usar changes (que se desaconseja su uso para fines no relacionados con la GUI).

Otros intentos han fallado, p.

zipE :: Num a => Event t a -> Event t b -> Event t (a,b) 
zipE ea eb = apply (stepper (0,) ((,) <$> ea)) eb 

resultados en los primeros elementos de las tuplas se cambiaron por uno - supongo que debido a la "ligero retraso" introducido por stepper. Pero no veo cómo obtener un comportamiento de un evento sin stepper (o accumB para el caso) y no veo cómo aplicar una función a un evento sin un comportamiento. Y, en general, no veo cómo proporcionar un valor inicial a Stepper en el caso de un tipo genérico.

+2

'ea' y' eb' no van a disparar al mismo tiempo. (Si sabe que van a disparar al mismo tiempo porque ambos se derivan del mismo evento subyacente, probablemente sea mejor reprocesar ese evento subyacente). ¿Qué quiere que suceda cuando uno dispara y el otro no? t? – dave4420

+0

Dave, tienes razón. Necesito un diseño diferente para mi red de eventos: -/Gracias por señalar esto. – copton

+2

De hecho, necesitaba una red de eventos diferente.Inicialmente, quise comprimir los dos eventos para alimentar las tuplas en 'f :: (a, b) -> IO()'. Lo que tengo ahora es 'f :: a -> b -> IO()' y 'reactimate $ (f <$> stepper 0 aE) <@> bE'. – copton

Respuesta

13

Tiene dificultades para definir zipE porque no tiene sentido semántico. Si se tiene en cuenta las definiciones semánticas

Event a == [(Time, a)] 
Event b == [(Time, b)] 

no hay una forma natural de la cremallera, porque, en general, cada evento será en un momento diferente.

Es posible comprimirlos utilizando una suma en lugar de un producto.

zipE :: Event a -> Event b -> Event (Either a b) 
zipE aE bE = (Left <$> aE) `mappend` (Right <$> bE) 

Si realmente necesita tener tanto a y b al mismo tiempo, la solución apropiada es crear un Behavior que se acumula con el uno o el otro, o la fusionado Either corriente que el anterior.

editar: un ejemplo de una forma de acumularse sobre la secuencia combinada (no comprobada). Otras implementaciones también son posibles. Esto no hace que ambos eventos ocurran al mismo tiempo, sino que le permite combinar el estado actual con los estados pasados ​​para que siempre tenga los últimos disponibles tanto de Left como de Right.

currentAandB :: a -> b -> Event a -> Event b -> Event (a,b) 
currentAandB a0 b0 aE bE = accumE (a0,b0) (mergefn <$> zipE aE bE) 
    where 
     mergefn (Left a) (_,b) = (a,b) 
     mergefn (Right b) (a,_) = (a,b) 

Todavía es necesario proporcionar valores iniciales para ambas corrientes Event. Piénselo de esta manera: si solo ha tenido eventos de Event a, ¿qué valor debería tener la segunda parte de la tupla?

+0

John, gracias por ayudarme. Me gustaría saber más sobre la respuesta apropiada que estás mencionando. ¿Podrían detallar cómo la acumulación de uno u otro hace que ambos eventos ocurran al mismo tiempo? Lo siento, si eso es obvio. Simplemente no lo veo. – copton

+1

Exactamente lo que sugeriría. – Conal

+0

@copton: He publicado un ejemplo de una forma de hacerlo. En cuanto a los valores iniciales, si piensas en el estado inicial de tu problema, probablemente encuentres una respuesta sensata. O puede usar una combinación de 'Maybe',' Data.Traventable.sequenceA' y 'filterJust' para producir solo resultados después de que haya recibido tanto' a' como 'b'. –

Cuestiones relacionadas