1

Finally beginning to dip my feet into the fascinating world of functional reactive programming. I come from a React-Redux background and want to try and implement some of the patterns I am familiar with there in a more pure FRP context. I am beginning to form some understanding of the nature and distinction between events and behaviors; questions like

have been helpful in this regard.

However, I am still very much a novice in these waters, and have a specific question that I am not sure these posts answer. (They might, and I might just be a bit "slow on the uptake," as they say) - I want to be able to react to an event when in conjunction with a particular value of a given behavior. I suppose another way of saying this, as I've indicated by the title, is that I want to know if and how this would work -

Event * Behavior -> Event

For example, in React-Redux, I might have an items list backed by some remote API fetch. I would have an InitializeAction get dispatched and reduced, resulting in an isLoading flag being updated in the current state (behavior - at this point in time, the items are loading); as well as the effectful reaction of the API fetch being initiated (event - which, being handled, results in the fetch). One way I could implement this is by merely watching isLoading and when it changes from false to true, initiate the fetch. So for example, connect a component's props to to isLoading and initiate the fetch out of that component's componentDidUpdate method.

However, this method is not quite what I am attempting to find. I suppose in a sense the state change could be made out as an event being the product of the InitializeAction event and the isLoading behavior, but merely observing the change in isLoading does not necessarily indicate the cause, merely the effect. This is nothing more than the formal logical fallacy of Affirming the Consequent, and would not help me if, say, some other action could also cause isLoading to transition from false to true. I want to have a specific LoadItems event generated specifically as a result of this specific InitializeAction event when the state is specifically isLoading == false.

The way I've handled this in React-Redux is to throw in the redux-loop middleware, which allows declarative effects to be an additional result from the reducer, as well as the updated state. So when isLoading == false, and InitializeAction occurs, the reducer's result is isLoading changing from false to true, as well as a (pure, as yet unevaluated) API-fetch effect declaration. The middleware then returns only the canonical updated state to Redux and executes the effect. I like this approach, it's easy to reason about, and one of the best attempts at managing effects in a pure way, declaratively. If, as I am given to believe, one of FRP's strengths is capably handling effects, I would be very interested to see how it handles this kind of situation.

Sorry for the essay. I am new to this subject and so my ability to articulate the issue likely leaves much to be desired. Any help would be greatly appreciated.

Edit for clarification

This is what I want. Event emitter A emits an Action event e_a. Behavior subject S with state b_t at time t computes, emits, stores the next state b_(t+1). Another event emitter F emits an eFfect event e_f that gets emitted when e_a is emitted while b_t is the current state. Both the updated state and the effect event can be subscribed to.

          S=b_t <------
           |           |
           v           |
A --e_a--> + --b_(t+1)-->...
           |
           v
           F --e_f-->...
Bondolin
  • 2,241
  • 6
  • 25
  • 51

1 Answers1

1

If I understand this right, what you want to achieve is the abilitiy to launch a fetch operation, set a isLoading flag to true as soon as the fetch is launched and set it to false as soon as the fetch returns or errors.

And you want to do this with Reactive style. In other words you want some clients to be able to subscribe to both the isLoading flag changes and the changes on the state produced by the result of the fetch.

If this is true, the following could be the schema of a solution based on RxJs.

// First of all define some "private" subjects
// Subjects are used to allow multicasting, i.e. more clients sharing the same stream
const _isLoading$ = new BehaviourSubject<boolean>(false);
const _state$ = new Subject<any>();

// Then define the "public" API streams, i.e. the streams the clients can subscribe to
// isLoading$ notifies when the isLoading value changes
export const isLoading$ = _isLoading$.asObservable();
// state$ notifies when the state value changes
export const state$ = _state$.asObservable();

// now we define a function to fire the fetch and the relative changes in isLoading
export fetch() = () => {
  // first we notify that isLoading is true
  _isLoading$.next(true)
  // then we fire the fetch operation
  from(fetch(....))  // from transforms a Promise to an Observable
  // pipe applies the transformations you need to the data notified by the upstream 
  // as well as fires side effects
  .pipe(
    // as soon as the fetch operation returns we notify the change in isLaoding
    tap({
      next: () => _isLoading$.next(false),
      error: () => _isLoading$.next(false) // you can do more if you want with the error
    }),
    // finally we apply the required transformations
    map(data => {
      const newState = // do anything you need to create the new state with the data fetched
      return newState
    }),
    // eventually you notify the changes in the state using the private Subject
    tap(_state$)
  )
}
Picci
  • 12,727
  • 9
  • 48
  • 85
  • The `tap` operator looks promising. The Moore I'm thinking about this, it seems what I'm trying to get at is an Rx Mealy machine :-) So output is a function of the input and source state in a state transition. I'll give this a more in-depth look soon. – Bondolin May 24 '21 at 05:39