haskell - Reactive Banana: consume parametrized call to an external API -
starting previous question here: reactive banana: how use values remote api , merge them in event stream
i have bit different problem now: how can use behaviour
output input io operation , display io operation's result?
below code previous answer changed second output:
import system.random type remotevalue = int -- generate random value within [0, 10) getremoteapivalue :: io remotevalue getremoteapivalue = (`mod` 10) <$> randomio getanotherremoteapivalue :: appstate -> io remotevalue getanotherremoteapivalue state = (`mod` 10) <$> randomio + count state data appstate = appstate { count :: int } deriving show transformstate :: remotevalue -> appstate -> appstate transformstate v (appstate x) = appstate $ x + v main :: io () main = start $ f <- frame [text := "appstate"] mybutton <- button f [text := "go"] output <- statictext f [] output2 <- statictext f [] set f [layout := minsize (sz 300 200) $ margin 10 $ column 5 [widget mybutton, widget output, widget output2]] let networkdescription :: forall t. frameworks t => moment t () networkdescription = ebt <- event0 mybutton command remotevalueb <- frompoll getremoteapivalue myremotevalue <- changes remotevalueb let events = transformstate <$> remotevalueb <@ ebt coreoftheapp :: behavior t appstate coreoftheapp = accumb (appstate 0) events sink output [text :== show <$> coreoftheapp] sink output2 [text :== show <$> reactimate ( getanotherremoteapivalue <@> coreoftheapp)] network <- compile networkdescription actuate network
as can see trying using new state of application -> getanotherremoteapivalue
-> show. doesn't work.
is possible doing that?
update based on erik allik , heinrich apfelmus below answers have current code situation - works :) :
{-# language scopedtypevariables #-} module main import system.random import graphics.ui.wx hiding (event, newevent) import reactive.banana import reactive.banana.wx data appstate = appstate { count :: int } deriving show initialstate :: appstate initialstate = appstate 0 transformstate :: remotevalue -> appstate -> appstate transformstate v (appstate x) = appstate $ x + v type remotevalue = int main :: io () main = start $ f <- frame [text := "appstate"] mybutton <- button f [text := "go"] output1 <- statictext f [] output2 <- statictext f [] set f [layout := minsize (sz 300 200) $ margin 10 $ column 5 [widget mybutton, widget output1, widget output2]] let networkdescription :: forall t. frameworks t => moment t () networkdescription = ebt <- event0 mybutton command remotevalue1b <- frompoll getremoteapivalue let remotevalue1e = remotevalue1b <@ ebt appstatee = accume initialstate $ transformstate <$> remotevalue1e appstateb = stepper initialstate appstatee mapio' :: (a -> io b) -> event t -> moment t (event t b) mapio' iofunc e1 = (e2, handler) <- newevent reactimate $ (\a -> iofunc >>= handler) <$> e1 return e2 remotevalue2e <- mapio' getanotherremoteapivalue appstatee let remotevalue2b = stepper nothing $ <$> remotevalue2e sink output1 [text :== show <$> appstateb] sink output2 [text :== show <$> remotevalue2b] network <- compile networkdescription actuate network getremoteapivalue :: io remotevalue getremoteapivalue = putstrln "getremoteapivalue" (`mod` 10) <$> randomio getanotherremoteapivalue :: appstate -> io remotevalue getanotherremoteapivalue state = putstrln $ "getanotherremoteapivalue: state = " ++ show state return $ count state
the fundamental problem conceptual one: frp events , behaviors can combined in pure way. in principle, not possible have function of type, say
mapio' :: (a -> io b) -> event -> event b
because order in corresponding io actions executed undefined.
in practice, may useful perform io while combining events , behaviors. execute
combinator can this, @erikallik indicates. depending on nature of getanotherremoteapivalue
, may right thing do, in particular if function idempotent, or quick lookup location in ram.
however, if computation more involved, better use reactimate
perform io computation. using newevent
create addhandler
, can give implementation of mapio'
function:
mapio' :: (a -> io b) -> event -> momentio (event b) mapio' f e1 = (e2, handler) <- newevent reactimate $ (\a -> f >>= handler) <$> e1 return e2
the key difference pure combinator
fmap :: (a -> b) -> event -> event b
is latter guarantees input , result events occur simultaneously, while former gives absolutely no guarantee when result event occurs in relation other events in network.
note execute
guarantees input , result have simultaneous occurrences, places informal restrictions on io allowed.
with trick of combining reactimate
newevent
similar combinator can written behaviors in similar fashion. keep in mind toolbox reactive.banana.frameworks
appropriate if dealing io actions precise order undefined.
(to keep answer current, have used type signatures upcoming reactive-banana 1.0. in version 0.9, type signature mapio'
is
mapio' :: frameworks t => (a -> io b) -> event t -> moment t (event t b)
)
Comments
Post a Comment