问题描述
我看过类似的问题和答案,但没有找到直接解决我问题的答案。我正在努力理解如何使用也许
或或者
或 Monads $ c $与管道功能一起使用。我想一起管道功能,但我想要管道停止并返回一个错误,如果发生在任何一步。我试图在一个node.js应用程序中实现函数式编程概念,这实际上是我第一次认真探索,所以没有答案会如此简单,以至于侮辱我关于这个主题的情报。
我写了一个像这样的管道函数:
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
//不要重新发明轮子
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中一起工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!