管道和monad如何在JavaScript中一起工作? [英] How do pipes and monads work together in JavaScript?

查看:138
本文介绍了管道和monad如何在JavaScript中一起工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看过类似的问题和答案,但没有找到直接解决我问题的答案。我正在努力理解如何使用也许或者 Monads

我写了一个像这样的管道函数:

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

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



$ p
$ b pre $ const $ token constutes = managejwt.maketoken)(x,y)


解决方案

钩子,线和沉子

我不能强调它是多么重要,你不会在所有感觉像你一样的新术语上受到阻碍必须学习 - 函数式编程是关于函数 - 也许您需要了解的唯一功能是它允许您使用参数来抽象部分程序;或者多个参数,如果需要的话(不是这样)并且由你的语言支持(通常是)

为什么我要告诉你这个?那么JavaScript已经有了一个非常好的API来使用内置的 Promise.prototype.then

  //不要重新发明轮子
const _pipe =(f,g)=> async(... args)=>等待g(等待f() (f).then(g).then(h)...


$。
myPromise.then b $ b

但是你想编写功能程序,对吧?这对功能程序员来说不成问题。隔离你想要抽象(隐藏)的行为,并简单地将它包装在一个参数化的函数中 - 现在你有一个函数,继续以功能风格编写你的程序...



在你做了一段时间之后,你开始注意到抽象的模式 - 这些模式将用作所有其他事物的用例(函数,应用程序,monads等),稍后您将了解 - 但现在将保存为 - 现在, 功能 ...



下面,我们通过 comp 演示异步函数的从左到右组合。就本程序而言, delay 包含在Promises创建者中, sq



常量延迟=(ms,x)=> new Promise(r => setTimeout(r,ms,x))const sq = async x => delay(1000,x * x)const add1 = async x =>延迟(1000,x + 1)//只是做一个函数const comp =(f,g)=> //摘掉疾病x =>然后(console.log,console.error)// 2秒钟后...(f) // 101

发明您自己的便利



您可以创建一个可接受任意数量函数的可变参数 compose - 也注意到这可以让您混合同步的异步函数 - 直接插入到 .then 中的好处,它自动将非Promise返回值提升为Promise



const delay =(ms,x)=> new Promise(r => setTimeout(r,ms,x))const sq = async x => delay(1000,x * x)const add1 = async x =>延迟(1000,x + 1)//使各种函数的变化效果= f => x => (f(x),x)//发明你自己的convenienceconst log = effect(console.log)const comp =(f,g)=> x => f(x).then(g)const compose =(... fs)=> fs.reduce(comp,x => Promise.resolve(x))//你的仪式是completecompose(log,add1,log,sq,log,add1,log,sq)然后(console.log,console.error)// 10 // 1秒以后... // 11 // 1秒以后... // 121 // 1秒以后... // 122 // 1秒后来...... // 14884

不难
$ b

comp 撰写是几乎不费力写作的易于理解的函数。因为我们使用了内置的 .then ,所有错误处理的东西都会自动连接到我们。您不必担心手动 await 'ing或 try / catch 。 catch 'ing - yet 另一个这样写函数的好处



/ strong>



现在,并不是说每次编写抽象的时候都是为了隐藏 bad ,但它可以对于各种任务非常有用 - 例如隐藏命令式,而

const append =(xs,x)=> xs.concat([x])const fibseq = n => {let seq = [] let a = 0 let b = 1 while(n> = 0){n = n-1 seq = append(seq,a)a = a + bb = a - b} return seq} console .log(fibseq(500))// [0,1,1,2,3,5,8,13,21,34,55,...]

但是你想编写功能程序,对不对?这对功能程序员来说不成问题。我们可以创建自己的循环机制,但这次它将使用函数和表达式而不是语句和副作用 - 所有这些都不会牺牲速度,可读性或 stack safety

这里, loop 使用我们的 recur 值容器。当函数返回一个非 recur 值时,计算完成,并返回最终值。



  const recur =(... values)=> ({type:recur,values})//有时会破坏规则;重塑更好的wheelconst循环= f => {acc = f()while(acc&& acc.type === recur)acc = f(... acc.values)return acc} const fibseq = x =>循环((n = x,seq = [],a = 0,b = 1)=> n === 0→seq.concat([a]):recur(n  -  1,seq.concat([a ]),a + b,a))console.time('loop / recur')console.log(fibseq(500))console.timeEnd('loop / recur')// [0,// 1,// 1,// 2,// 3,// 5,// 8,// 13,// 21,// 34,// ... 490 more items] // loop / recur:5ms  



$ p $ <$> $ $
$ b

请记住,你可以做任何你想做的事。对于然后没有什么不可思议的 - 某人在某个地方决定做出来。你可能是某个地方的某个人,只需制作你自己的然后 - 这里然后是一种前向合成函数 - 就像 Promise.prototype.then ,它会自动将然后应用于非 - ,然后返回值;我们补充说这并不是因为这是一个特别好的主意,而是为了表明我们可以做出这样的行为,如果我们想要的话当您理解时,那么,您将会理解母亲的所有单子 - 记住专注于力学 - 而不是术语

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

b $ b

那是什么语言?



它甚至不再像JavaScript一样 - 谁在乎?它是你的程序和决定你想要它的样子。一种好的语言不会妨碍你的发展,并迫使你用任何特定风格编写你的程序;功能性的或其他的

它实际上是JavaScript,只是由于它能够表达的错误概念而不受限制

 ($)$($)$($) / 103 
// => {然后:103}

sq(5)(log)(add1)(log)
// 25
// 26
// => {Then:26}

出货



我们在本地代码片段中使用了 comp 撰写的名称,但是当您打包你的程序,你应该根据你的具体情况选择有意义的名字 - 参见Bergi对推荐的评论

I have looked at similar questions and answers and have not found an answer that directly addresses my question. I am struggling to understand how to use Maybe or Eitheror Monads in conjunction with piping functions. I want to pipe functions together, but I want the pipe to stop and return an error if one occurs at any step. I am trying to implement Functional Programming concepts in a node.js app, and this is really my first serious exploration of either, so no answer will be so simple as to insult my intelligence on the subject.

I have written a pipe function like this:

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

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

I am calling it like this:

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

解决方案

hook, line and sinker

I can't stress how critical it is that you don't get snagged on all the new terms it feels like you have to learn – functional programming is about functions – and perhaps the only thing you need to understand about the function is that it allows you to abstract part of your program using a parameter; or multiple parameters if needed (it's not) and supported by your language (it usually is)

Why am I telling you this? Well JavaScript already has a perfectly good API for sequencing asynchronous functions using the built-in, Promise.prototype.then

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

But you want to write functional programs, right? This is no problem for the functional programmer. Isolate the behavior you want to abstract (hide), and simply wrap it in a parameterized function – now that you have a function, resume writing your program in a functional style ...

After you do this for a while, you start to notice patterns of abstraction – these patterns will serve as the use cases for all the other things (functors, applicatives, monads, etc) you learn about later – but save those for later – for now, functions ...

Below, we demonstrate left-to-right composition of asynchronous functions via comp. For the purposes of this program, delay is included as a Promises creator, and sq and add1 are sample async functions.

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

invent your own convenience

You can make a variadic compose that accepts any number of functions – also notice how this allows you to mix sync and async functions in the same composition – a benefit of plugging right into .then, which automatically promotes non-Promise return values to a Promise

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
compose (log, add1, log, sq, log, add1, log, sq) (10)

  // effect added for demo
  .then (console.log, console.error)

  // 10
  // 1 second later ...
  // 11
  // 1 second later ...
  // 121
  // 1 second later ...
  // 122
  // 1 second later ...
  // 14884

work smarter, not harder

comp and compose are easy-to-digest functions that took almost no effort to write. Because we used built-in .then, all the error-handling stuff gets hooked up for us automatically. You don't have to worry about manually await'ing or try/catch or .catch'ing – yet another benefit of writing our functions this way

no shame in abstraction

Now, that's not to say that every time you write an abstraction it's for the purposes of hiding something bad, but it can be very useful for a variety of tasks – take for example "hiding" the imperative-style while.

const append = (xs, x) =>
  xs.concat ([x])

const fibseq = n => {
  let seq = []
  let a = 0
  let b = 1
  while (n >= 0) {
    n = n - 1
    seq = append (seq, a)
    a = a + b
    b = a - b
  }
  return seq
}

console.log (fibseq (500))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...  ]

But you want to write functional programs, right? This is no problem for the functional programmer. We can make our own looping mechanism but this time it will use functions and expressions instead of statements and side effects – all without sacrificing speed, readability, or stack safety.

Here, loop continuously applies a function using our recur value container. When the function returns a non-recur value, the computation is complete, and the final value is returned.

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

// break the rules sometimes; reinvent a better wheel
const loop = f =>
  {
    let acc = f ()
    while (acc && acc.type === recur)
      acc = f (...acc.values)
    return acc
  }
      
const fibseq = x =>
  loop ((n = x, seq = [], a = 0, b = 1) =>
    n === 0
      ? seq.concat ([a])
      : recur (n - 1, seq.concat ([a]), a + b, a))

console.time ('loop/recur')
console.log (fibseq (500))
console.timeEnd ('loop/recur')

// [ 0,
//   1,
//   1,
//   2,
//   3,
//   5,
//   8,
//   13,
//   21,
//   34,
//   ... 490 more items ]
// loop/recur: 5ms

nothing is sacred

And remember, you can do whatever you want. There's nothing magical about then – someone, somewhere decided to make it. You could be somebody in some place and just make your own then – here then is a sort of forward-composition function – just like Promise.prototype.then, it automatically applies then to non-then return values; we add this not because it's a particularly good idea, but to show that we can make that kind of behavior if we wanted to

When you understand then, you will have understood the mother of all monads – remember to focus on the mechanics – not the terms

const then = x =>
  x && x.type === then
    ? x
    : Object.assign (f => then (f (x)), { type: 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

what language is that?

It doesn't even look like JavaScript anymore – who cares? It's your program and you decide what you want it to look like. A good language won't stand in your way and force you to write your program in any particular style; functional or otherwise

It's actually JavaScript, just uninhibited by misconceptions of what its capable of expressing

then (10) (log) (sq) (log) (add1) (add1) (add1) (log)
// 10
// 100
// 103
// => { Then: 103 }

sq (5) (log) (add1) (log)
// 25
// 26
// => { Then: 26 }

ship it

We just used the names comp and compose in our local snippets, but when you package your program, you should pick names that make sense given your specific context – see Bergi's comment for a recommendation

这篇关于管道和monad如何在JavaScript中一起工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆