Well they don't need to be idempotent, but it's generally a good idea if you want to stay sane debugging your system. To the second effect, I know in clojure's reframe this is handled by something called "subscriptions". It provides a sort of extension of memoization where functions subscribe to a state atom and you can create this dependency graph.
I'll be honest, I haven't used reframe myself, I just came across the concept while using cljfx
https://github.com/cljfx/cljfx/#subscriptions-and-contexts
so I'm not quite sure if it's all updated async or on each render - but the framework is very powerful and simplifies application logic by an order of magnitude.
But in both reframe and cljfx it's tied to the gui rendering which is a shame - b/c as you comment suggests - this is a more general purpose pattern. In-library it remains a bit too verbose with lots of boiler plate and api to remember and it's all a bit too difficult to reason about at times. It seems like it should be something that's part of the language "runtime". However in the end it still works through memoized functions, which is a bit more explicit that a second magical assignment operator :)
Anyway, a bit of a ramble.. thanks for the info :)