вообще:

мотрел на похожие вопросы и ответы и не нашел ответа, который непосредственно касается моего вопроса. Я изо всех сил пытаюсь понять, как использоватьMaybe или жеEitherили жеMonads в сочетании с функциями трубопровода. Я хочу соединить функции вместе, но я хочу, чтобы конвейер остановился и возвратил ошибку, если она возникнет на любом этапе. Я пытаюсь реализовать концепции функционального программирования в приложении node.js, и это действительно мое первое серьезное исследование, так что ни один ответ не будет настолько простым, чтобы оскорбить мой интеллект по этому вопросу.

Я написал функцию канала следующим образом:

const _pipe = (f, g) => async (...args) => await g( await f(...args))

module.exports = {arguments.
    pipeAsync: async (...fns) => {
        return await fns.reduce(_pipe)
    }, 
...

Я называю это так:

    const token = await utils.pipeAsync(makeACall, parseAuthenticatedUser, syncUserWithCore, managejwt.maketoken)(x, y)  
 user63318324 окт. 2017 г., 20:53
Нет«труба» ниMonads
 Bergi24 окт. 2017 г., 21:46
Вы ищетеодновалентныйchainне обычная функция композиции.await только помогает с обещаниями.
 JLRishe14 янв. 2018 г., 19:04
Это заблудилосьarguments. в строке 3 опечатка или какой-то синтаксис, который я никогда раньше не видел?

Ответы на вопрос(2)

Решение Вопроса

крюк, леска и грузило

Я не могу не подчеркнуть, насколько важно, чтобы вы не цеплялись за все новые термины, которые, как вам кажется, нужно выучить - функциональное программирование - этофункции - и, возможно, единственное, что вам нужно понять о функции, это то, что она позволяет вам абстрагировать часть вашей программы с помощью параметра; или несколько параметров, если необходимо (это не так) и поддерживается вашим языком (обычно это так)

Почему я говорю тебе это? Ну, JavaScript уже имеет очень хороший API для секвенирования асинхронных функций с помощью встроенного,Promise.prototype.then

// never reinvent the wheel
<del>const _pipe = (f, g) => async (...args) => await g( await f(...args))</del>
myPromise .then (f) .then (g) .then (h) ...

Но вы хотите писать функциональные программы, верно? Это не проблема для функционального программиста. Выделите поведение, которое вы хотите абстрагировать (скрыть), и просто оберните его в параметризованныйфункция - теперь, когда у вас есть функция, возобновите написание вашей программы в функциональном стиле ...

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

Ниже мы демонстрируемслева направо состав асинхронных функций черезcomp, Для целей этой программы,delay включен как создатель Обещаний, иsq а такжеadd1 примерные асинхронные функции -

const delay = (ms, x) =>
  new Promise (r => setTimeout (r, ms, x))

const sq = async x =>
  delay (1000, x * x)
  
const add1 = async x =>
  delay (1000, x + 1)

// just make a function  
const comp = (f, g) =>
  // abstract away the sickness
  x => f (x) .then (g)

// resume functional programming  
const main =
  comp (sq, add1)

// print promise to console for demo
const demo = p =>
  p .then (console.log, console.error)

demo (main (10))
// 2 seconds later...
// 101

придумать собственное удобство

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

const delay = (ms, x) =>
  new Promise (r => setTimeout (r, ms, x))

const sq = async x =>
  delay (1000, x * x)
  
const add1 = async x =>
  delay (1000, x + 1)

// make all sorts of functions
const effect = f => x =>
  ( f (x), x )

// invent your own convenience
const log =
  effect (console.log)
  
const comp = (f, g) =>
  x => f (x) .then (g)

const compose = (...fs) =>
  fs .reduce (comp, x => Promise .resolve (x))
  
// your ritual is complete
const main =
  compose (log, add1, log, sq, log, add1, log, sq)

// print promise to console for demo
const demo = p =>
  p .then (console.log, console.error)

demo (main (10))
// 10
// 1 second later ...
// 11
// 1 second later ...
// 121
// 1 second later ...
// 122
// 1 second later ...
// 14884

Работай умом, а не силой

comp а такжеcompose это простые в освоении функции, которые практически не требуют усилий для написания. Потому что мы использовали встроенный.thenвсе средства обработки ошибок подключаются к нам автоматически. Вам не нужно беспокоиться о вручнуюawaitилиtry/catch или же.catchИнг - покаеще один Преимущество написания наших функций таким образом -

нет ничего постыдного в абстракции

Это не значит, что каждый раз, когда вы пишете абстракцию, это делается для того, чтобы что-то скрытьПлохо, но это может быть очень полезно для множества задач - например, «скрытие» императивного стиляwhile -

const fibseq = n => // a counter, n
{ let seq = []      // the sequence we will generate
  let a = 0         // the first value in the sequence
  let b = 1         // the second value in the sequence
  while (n > 0)     // when the counter is above zero
  { n = n - 1             // decrement the counter
    seq = [ ...seq, a ]   // update the sequence
    a = a + b             // update the first value
    b = a - b             // update the second value
  }
  return seq        // return the final sequence
}

console .time ('while')
console .log (fibseq (500))
console .timeEnd ('while')
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...  ]
// while: 3ms

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

Вот,loop постоянно применяет функцию с помощью нашегоrecur контейнер значений. Когда функция возвращает неrecur значение, вычисление завершено, и возвращается окончательное значение.fibseq является чистым, функциональным выражением, полным неограниченной рекурсии. Обе программы вычисляют результат всего за 3 миллисекунды. Не забудьте проверить соответствие ответов: D

const recur = (...values) =>
  ({ recur, values })

// break the rules sometimes; reinvent a better wheel
const loop = f =>
{ let acc = f ()
  while (acc && acc.recur === recur)
    acc = f (...acc.values)
  return acc
}
      
const fibseq = x =>
  loop               // start a loop with vars
    ( ( n = x        // a counter, n, starting at x
      , seq = []     // seq, the sequence we will generate
      , a = 0        // first value of the sequence
      , b = 1        // second value of the sequence
      ) =>
        n === 0      // once our counter reaches zero
          ? seq      // return the sequence
          : recur    // otherwise recur with updated vars
              ( n - 1          // the new counter
              , [ ...seq, a ]  // the new sequence
              , b              // the new first value
              , a + b          // the new second value
              )
    )

console.time ('loop/recur')
console.log (fibseq (500))
console.timeEnd ('loop/recur')
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...  ]
// loop/recur: 3ms

ничто не является священным

И помните, вы можете делать все, что хотите. В этом нет ничего волшебногоthen - кто-то где-то решил это сделать. Вы можете быть кем-то в каком-то месте и просто сделать свой собственныйthen - Вотthen это своего рода функция прямой композиции - так же, какPromise.prototype.then, это автоматически применяетсяthen не-then возвращаемые значения; мы добавляем это не потому, что это особенно хорошая идея, а чтобы показать, что мы можем вести себя подобным образом, если захотим.

const then = x =>
  x && x.then === then
    ? x
    : Object .assign
        ( f => then (f (x))
        , { then }
        )
  
const sq = x =>
  then (x * x)
  
const add1 = x =>
  x + 1
  
const effect = f => x =>
  ( f (x), x )
  
const log =
  effect (console.log)
  
then (10) (log) (sq) (log) (add1) (add1) (add1) (log)
// 10
// 100
// 101

sq (2) (sq) (sq) (sq) (log)
// 65536

что это за язык?

Он даже больше не похож на JavaScript, но кого это волнует? Этотвой программа ивы решить, как вы хотите, чтобы он выглядел. Хороший язык не встанет у вас на пути и не заставит вас писать свою программу наЛюбые особый стиль; функциональный или иным образом.

Это на самом деле JavaScript, просто не поддавшийся ошибочным представлениям о том, что он способен выразить -

const $ = x => k =>
  $ (k (x))
  
const add = x => y =>
  x + y

const mult = x => y =>
  x * y
  
$ (1)           // 1
  (add (2))     // + 2 = 3
  (mult (6))    // * 6 = 18
  (console.log) // 18
  
$ (7)            // 7
  (add (1))      // + 1 = 8
  (mult (8))     // * 8 = 64
  (mult (2))     // * 2 = 128
  (mult (2))     // * 2 = 256
  (console.log)  // 256

Когда понимаешь$, вы понялимать всех монад, Не забудьте сосредоточиться на механике и получить интуицию длякак это устроено; меньше беспокоиться об условиях.

отправим его

Мы просто использовали именаcomp а такжеcompose в наших локальных фрагментах, но когда вы упаковываете свою программу, вы должны выбрать имена, которые имеют смысл с учетом вашего конкретного контекста - см. рекомендацию Берги.

 Danny Ellis Jr.25 окт. 2017 г., 01:44
Спасибо за Ваш ответ. Некоторые вещи до сих пор неясны. Пожалуйста, не увлекайтесь асинхронностью / ожиданием. Я добавил это, потому что некоторые из моих функций, которые мне нужно составить, были разработаны как асинхронные, и они не будут работать, если не будут полностью асинхронными. Я предпочел этот синтаксис, потому что 1) я знаком с ним из C #, и 2 это самый последний способ в nodejs. Если мне нужно сделать это с «тогда», я внесу это изменение. Я полагаю, что я смешиваю слишком много вещей, которые я не понимаю. Если бы мы исключили асинхронность, то как бы вы прервали канал при ошибках?
 user63318325 окт. 2017 г., 17:15
async/await является Promise.prototype.then за кадром - это просто другоесинтаксис - мымог бы использоватьawait но дело в том, чтобы показать, что такая тяжелая техника не нужна; это становится излишним, когда мы получаем * точный поведение, которое мы хотим с простымf(x).then(g)
 Bergi24 окт. 2017 г., 21:50
Вы должны назвать этоcompAsync отличить его от обычной функции композиции. Или дажеcompKleisli если хочешь быть модным :-)
 user63318325 окт. 2017 г., 17:18
.... (прод.): Делать это другим способом абсолютно нормально, но ваши причины для этого не хороши; 1) синтаксис из другого языка (C #) не влияет на то, что JS способен выразить; 2) как другие выбирают писать свои программы,"последний путь в nodejs", не имеет прямого влияния на ваши предпочтения - и не имеетпоследний подразумеватьлучше всего - когда CoffeeScript был * последним способоммногие люди переводили очень хорошие базы JavaScript-программ на тупиковый язык.
 user63318324 окт. 2017 г., 21:56
@ Bergi, о, я хотел упомянуть об экспорте его под другим именем - спасибо за замечание. Я сделаю небольшое редактирование.

но, похоже, она не удосужилась ответить на ваш вопрос.

Короткий ответ: ваш_pipe Функция распространяет ошибки просто отлично. И прекращает запуск функций, как только выдает ошибку.

Проблема с вашимpipeAsync функция, где у вас была правильная идея, но у вас нет необходимости возвращатьобещание для функции вместо функции.

Вот почему вы не можете сделать это, потому что каждый раз выдает ошибку:

const result = await pipeAsync(func1, func2)(a, b);

Для того, чтобы использоватьpipeAsync в его текущем состоянии вам понадобится дваawaits: один, чтобы получить результатpipeAsync и один, чтобы получить результат вызова этого результата:

const result = await (await pipeAsync(func1, func2))(a, b);

Решение

Удалить ненужноеasync а такжеawait из определенияpipeAsync, Составление ряда функций, даже асинхронных, не является асинхронной операцией:

module.exports = {
    pipeAsync: (...fns) => fns.reduce(_pipe),

После того, как вы это сделали, все работает хорошо:

const _pipe = (f, g) => async(...args) => await g(await f(...args))
const pipeAsync = (...fns) => fns.reduce(_pipe);

const makeACall = async(a, b) => a + b;
const parseAuthenticatedUser = async(x) => x * 2;
const syncUserWithCore = async(x) => {
  throw new Error("NOOOOOO!!!!");
};
const makeToken = async(x) => x - 3;

(async() => {
  const x = 9;
  const y = 7;

  try {
    // works up to parseAuthenticatedUser and completes successfully
    const token1 = await pipeAsync(
      makeACall,
      parseAuthenticatedUser
    )(x, y);
    console.log(token1);

    // throws at syncUserWithCore
    const token2 = await pipeAsync(
      makeACall,
      parseAuthenticatedUser,
      syncUserWithCore,
      makeToken
    )(x, y);
    console.log(token2);
  } catch (e) {
    console.error(e);
  }
})();

Это также может быть написано без использованияasync вообще:

const _pipe = (f, g) => (...args) => Promise.resolve().then(() => f(...args)).then(g);
const pipeAsync = (...fns) => fns.reduce(_pipe);

const makeACall = (a, b) => Promise.resolve(a + b);
const parseAuthenticatedUser = (x) => Promise.resolve(x * 2);
const syncUserWithCore = (x) => {
  throw new Error("NOOOOOO!!!!");
};
const makeToken = (x) => Promise.resolve(x - 3);

const x = 9;
const y = 7;

// works up to parseAuthenticatedUser and completes successfully
pipeAsync(
  makeACall,
  parseAuthenticatedUser
)(x, y).then(r => console.log(r), e => console.error(e));

// throws at syncUserWithCore
pipeAsync(
  makeACall,
  parseAuthenticatedUser,
  syncUserWithCore,
  makeToken
)(x, y).then(r => console.log(r), e => console.error(e))

Ваш ответ на вопрос