2012-07-31 16 views
7

Sono nuovo alla banana reattiva e al FRP in generale, quindi mi scuso se mi manca qualcosa di ovvio.Implementazione di zipE :: Evento t a -> Evento t b -> Evento t (a, b)

Per my project (a front-end GDB/MI) Utilizzo banana reattiva (versione 0.6.0.0) sia per la GUI che per i moduli logici front-end. Il primo funziona alla grande ma per il secondo ho apparentemente bisogno di ulteriori combinatori.

Uno di questi è zipE :: Event t a -> Event t b -> Event t (a, b). Purtroppo, tutto quello che potevo venire in mente è una soluzione in monade NetworkDescription che utilizza changes e non è generica nelle tipologie di evento:

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

Naturalmente, io non sono soddisfatto di questo. Quindi volevo chiedere come implementare una generica funzione zipE senza usare changes (che è sconsigliato usare per scopi non-GUI).

Altri tentativi non riusciti, ad es.

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

risultati dei primi elementi delle tuple di essere spostati da una - Credo che a causa della "leggero ritardo" introdotto da stepper. Ma non vedo come ottenere un comportamento da un evento senza stepper (o accumB per quella questione) e non vedo come applicare una funzione a un evento senza un comportamento. E nel complesso, non vedo come fornire un valore iniziale per il passo passo nel caso di un tipo generico.

+2

'ea' e' eb' non spareranno allo stesso tempo. (Se sai che stanno per sparare allo stesso tempo perché sono entrambi derivati ​​dallo stesso evento sottostante, è probabilmente meglio rielaborare quell'evento sottostante.) Cosa vuoi che succeda quando uno spara e l'altro no? t? – dave4420

+0

Dave, hai ragione. Ho bisogno di un design diverso per la mia rete di eventi: -/Grazie per averlo indicato. – copton

+2

Ho effettivamente richiesto una rete di eventi diversa.Inizialmente, volevo comprimere i due eventi per alimentare le tuple in 'f :: (a, b) -> IO()'. Quello che ho ora è invece 'f :: a -> b -> IO()' e 'reactimate $ (f <$> stepper 0 aE) <@> bE'. – copton

risposta

13

Hai difficoltà a definire zipE perché non ha senso semantico. Se si considerano le definizioni semantiche

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

non c'è un modo naturale per zip loro, perché in generale ogni evento sarà in un momento diverso.

È possibile comprimerli utilizzando una somma anziché un prodotto.

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

Se davvero necessario avere sia a e b, allo stesso tempo, la soluzione appropriata è quello di creare un Behavior che si accumula su uno o l'altro, o la fusione Either flusso come sopra.

modifica: un esempio di un modo per accumulare sul flusso unito (non testato). Sono possibili anche altre implementazioni. Questo non fa sì che entrambi gli eventi si verifichino contemporaneamente, ma ti consente di combinare lo stato corrente con gli stati passati in modo da avere sempre il più recente disponibile sia dei valori Left sia di .

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) 

E 'ancora necessario fornire i valori iniziali per entrambi i flussi Event. Pensaci in questo modo: se hai avuto solo eventi da Event a, quale valore dovrebbe avere la seconda parte della tupla?

+0

John, grazie per avermi aiutato. Mi piacerebbe sapere di più sulla risposta appropriata che stai citando. Potresti per favore approfondire in che modo l'accumulo sull'uno o sull'altro fa sì che entrambi gli eventi si verifichino contemporaneamente? Scusa, se è ovvio. Io proprio non lo vedo. – copton

+1

Esattamente quello che suggerirei. – Conal

+0

@copton: ho pubblicato un esempio di un modo per farlo. Per quanto riguarda i valori iniziali, se pensi allo stato iniziale del tuo problema, probabilmente troverai una risposta ragionevole. Oppure puoi usare una combinazione di 'Maybe',' Data.Traversable.sequenceA' e 'filterJust' per produrre output solo dopo aver ricevuto sia un' a' che un 'b'. –

Problemi correlati