Рамда: Есть ли способ «разветвлять» параметр на две функции во время конвейера? - PullRequest
0 голосов
/ 20 февраля 2019

Я начинающий функциональный программист.Я работаю над приложением React Native, используя Ramda.Приложение позволяет пользователям обслуживать свои дома.

Я написал функцию под названием asyncPipe, которая позволяет мне передавать обещания и обычные функции.Я использую его для loginFlow, который в настоящее время имеет http-запрос (getHouseList) в качестве последней функции.

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);

Итак, после входа в приложение приложение загружает дома пользователя.Теперь, в зависимости от того, есть ли у него только один или несколько домов, я бы хотел отправить пользователя либо в виде списка, чтобы выбрать дом, либо в детальный вид, если у него только один дом.Кроме того, я хотел бы отправить действие Redux, чтобы сохранить список в моем редукторе, и другое действие, чтобы выбрать дом, если есть только один.

В настоящее время я делаю это так:

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

Но, как вы видите, это супер императив.Похоже, мне нужно «разветвлять» список и использовать его дважды в конвейере (потому что Redux dispatch не имеет возвращаемого значения).

Мой главный вопрос:

Как сделать это более функциональным / декларативным (если есть способ)?

Небольшой подвопрос Iдолжно быть, хорошо ли это быть обязательным здесь / если делать это функционально - хорошая идея.

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Учитывая, что мы можем использовать R.then и R.otherwise, тогда asyncPipe на самом деле не требуется.Одним из принципов функционального программирования на самом деле является делегирование оркестровки ...

Наконец, конечно, вы можете быть более декларативным, и хороший способ начать - попытаться избежать императивных потоков управления.R.ifElse определенно поможет вам здесь:)

Если у вашего кода есть побочные эффекты, тогда используйте R.tap в ваших каналах:)

const fake = cb => () => cb([
  { name: 'Hitmands', id: 1 },
  { name: 'Giuseppe', id: 2 },
]);

const fakeApiCall = () => new Promise(resolve => setTimeout(fake(resolve), 500));
const dispatch = action => data => console.log(`dispatch("${action}")`, data);
const navigate = view => data => console.log(`navigate("${view}")`, data);

const loginFlow = (...fns) => R.pipe(
  R.tap(() => console.log('login Flow Start')),
  fakeApiCall,
  R.then(R.pipe(
    ...fns,
    R.tap(() => console.log('login Flow End')),
  )),
)

const flow = loginFlow(
  R.tap(dispatch('addHouse')), // use tap for side effects
  R.ifElse(
    R.pipe(R.length, R.gt(R.__, 1)), // result.length > 1
    R.tap(navigate('ListScreen')), // onTrue
    R.pipe( // onFalse
      R.tap(dispatch('pickHouse')),
      R.tap(navigate('DetailScreen')),
    ),
  ),
);

/* await */ flow();

/** UPDATES **/
const isXGreaterThan1 = R.gt(R.__, 1);
const isListLengthGreatherThanOne = R.pipe(R.length, isXGreaterThan1);

console.log(`is list.length > 1`, isListLengthGreatherThanOne([1, 2, 3]));
console.log(`is list.length > 1`, isListLengthGreatherThanOne([1]));
console.log(`is list.length > 1`, isListLengthGreatherThanOne([]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
0 голосов
/ 20 февраля 2019

Возможно, вы могли бы расширить свой асинхронный конвейер, используя что-то вроде 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)
);

То, стоит ли это делать, будет зависеть от вашего приложения.Если конвейер уже длинный, то, возможно, было бы чище добавить что-то в конец, даже если это не особо функциональные разделы.Но для короткого конвейера это может не иметь особого смысла.

Возможно, вы захотите взглянуть на устаревший pipeP или его замену pipeWith (then).

Но вы спросили в заголовке о разветвлении параметра.Рамда converge делает именно это:

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

Это также позволяет передавать более двух функций и передавать более одного параметра в результирующую функцию:

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