Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ramda: Is there a way to 'fork' a parameter to two functions during pipe?

I'm a functional programming beginner. I'm working on a React Native app using Ramda. The app lets users maintain their houses.

I have written function called asyncPipe which lets me pipe promises and normal functions. I use it for the loginFlow which currently has a http request (getHouseList) as its last function.

const asyncPipe = (...fns) => x => fns.reduce(async (y, f) => f(await y), x);

const loginFlow = asyncPipe(
  // ... someFunctions
  getHouseList
);

// used later like this in LoginForm.js's handleSubmit():
const list = await loginFlow(credentials);

So, after logging in, the app loads the user's houses. Now depending on whether he has only one or multiple houses I would like to send the user either to list view to choose a house or a detail view if he only has one house. Additionally, I would like to dispatch a Redux action to save the list in my reducer and another action to pick the house if there is only one.

Currently I do it like this:

const list = await loginFlow(credentials);
dispatch(addHouses(list));
if (list.length > 1) {
  navigate('ListScreen')
} else {
  dispatch(pickHouse(list[0]);
  navigate('DetailScreen') ;
}

But as you can see that is super imperative. It seems like I have to 'fork' the list and use it twice in the pipe (because Redux' dispatch does not have a return value).

My main question is:

How to do this more functional / declaratively (if there is a way)?

A little sub question I have would be, whether its okay to be imperative here / if doing it functional is a good idea.

like image 249
J. Hesters Avatar asked Oct 14 '25 07:10

J. Hesters


1 Answers

You could probably extend your async pipeline, using something like tap:

const loginFlow = asyncPipe(
  // ... some functions
  getHouseList,
  tap(compose(dispatch, addHouses)),
  tap(unless(list => list.length > 1, list => dispatch(pickHouse(list[0])))),
  list => navigate(list.length > 1 ? 'ListScreen' : 'DetailScreen', list)
);

Whether this is worth doing will depend upon your application. If the pipeline is already a longish one, then it would probably be cleaner to add things to the end this way, even if they're not particularly functional sections. But for a short pipeline, this might not make much sense.

You also might want to look at the now-deprecated, pipeP or its replacement, pipeWith(then).

But you asked in the title about forking a parameter. Ramda's converge does exactly that:

converge(f, [g, h])(x) //=> f(g(x), h(x))

This allows you to pass more than two functions as well, and to pass more than one parameter to the resulting function:

converge(f, [g, h, i])(x, y) //=> f(g(x, y), h(x, y), i(x, y)) 
like image 176
Scott Sauyet Avatar answered Oct 17 '25 23:10

Scott Sauyet