I would like to create a Behavior t a
from an IO a
, with the intended semantics that the IO action would be run every time the behavior is sample
I've been experimenting with this for a while and found a workaround. It seems to work with the latest version of reflex to date. The trick is to forcefully invalidate the cached value every time you evaluate a given IO
action.
import qualified Reflex.Spider.Internal as Spider
onDemand :: IO a -> Behavior t a
onDemand ma = SpiderBehavior . Spider.Behavior
. Spider.BehaviorM . ReaderT $ computeF
where
computeF (Nothing, _) = unsafeInterleaveIO ma
computeF (Just (invW,_), _) = unsafeInterleaveIO $ do
toReconnect <- newIORef []
_ <- Spider.invalidate toReconnect [invW]
ma
It is important to use unsafeInterleaveIO
to run the invalidator as late as possible, so that it invalidates an existing thing.
There is another problem with this code: I ignore toReconnect
reference and the result of invalidate
function. In current version of reflex, the latter is always empty, so it should not cause any problems.
But I am not sure about toReconnect
: from the code, it seems that if it has some subscribed switches, they might break if not treated properly. Though I am not sure if this kind of behavior can have switches subscribed or not.
UPDATE for those who really want to implement this: The code above can deadlock in some complicated setups. My solutions was to perform invalidation slightly after the computation itself in a separate thread. Here is the complete code snippet. The solution by the link seems to work correctly (using it for almost a year now in production).