¿Cómo funcionan las tuberías y las mónadas juntas en JavaScript?

He observado preguntas y respuestas similares y no he encontrado una respuesta que aborde directamente mi pregunta. Estoy luchando por comprender cómo usar Maybe o Either o Monads junto con las funciones de tuberías. Quiero canalizar funciones juntas, pero quiero que la tubería se detenga y devuelva un error si ocurre en cualquier paso. Estoy tratando de implementar conceptos de Progtwigción Funcional en una aplicación node.js, y esta es realmente mi primera exploración seria de cualquiera, por lo que ninguna respuesta será tan simple como para insultar mi inteligencia sobre el tema.

He escrito una función de tubería como esta:

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

Lo estoy llamando así:

  const token = await utils.pipeAsync(makeACall, parseAuthenticatedUser, syncUserWithCore, managejwt.maketoken)(x, y) 

anzuelo, línea y plomo

No puedo enfatizar cuán importante es que no te enganchen en todos los nuevos términos, se siente como que tienes que aprender, la progtwigción funcional se trata de funciones , y tal vez lo único que necesites entender sobre la función es que le permite abstraer parte de su progtwig usando un parámetro; o múltiples parámetros si es necesario (no lo es) y es compatible con su idioma (generalmente lo es)

¿Por qué te estoy diciendo esto? Bueno, JavaScript ya tiene una API perfectamente buena para secuenciar funciones asincrónicas usando el Promise.prototype.then

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

Pero quieres escribir progtwigs funcionales, ¿verdad? Esto no es problema para el progtwigdor funcional. Aísle el comportamiento que desea abstraer (ocultar) y simplemente envuélvalo en una función parametrizada; ahora que tiene una función, reanude la escritura de su progtwig en un estilo funcional …

Después de hacer esto por un tiempo, comienzas a notar patrones de abstracción: estos patrones servirán como casos de uso para todas las demás cosas (funtores, aplicativos, mónadas, etc.) de las que aprenderás más tarde, pero guarda las para más adelante . ahora, funciones

A continuación, mostramos la composición de izquierda a derecha de las funciones asíncronas a través de comp . A los fines de este progtwig, la delay se incluye como creador de Promises, y sq y add1 son funciones asíncronas de muestra.

 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 comp (sq, add1) (10) // this effect added for demo purposes .then (console.log, console.error) // 2 seconds later... // 101 

La respuesta de naomik es muy interesante, pero no parece que haya contestado tu pregunta.

La respuesta corta es que tu función _pipe propaga errores muy bien. Y deja de ejecutar funciones tan pronto como uno arroja un error.

El problema es con su función pipeAsync , donde tuvo la idea correcta, pero innecesariamente le devuelve la promesa de una función en lugar de una función.

Es por eso que no puedes hacer esto, porque arroja un error cada vez:

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

Para usar pipeAsync en su estado actual, necesitaría dos s: uno para obtener el resultado de pipeAsync y otro para obtener el resultado de llamar a ese resultado:

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

La solución

Elimine la sincronización async innecesaria y await de la definición de pipeAsync . El acto de componer una serie de funciones, incluso funciones asincrónicas, no es una operación asincrónica:

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

Una vez que hayas hecho eso, todo funciona bien:

 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); } })(); 
    Intereting Posts