Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Simple Node/Express应用程序,功能性编程方式(如何在JavaScript中处理副作用?)_Javascript_Node.js_Mongodb_Express_Functional Programming - Fatal编程技术网

Simple Node/Express应用程序,功能性编程方式(如何在JavaScript中处理副作用?)

Simple Node/Express应用程序,功能性编程方式(如何在JavaScript中处理副作用?),javascript,node.js,mongodb,express,functional-programming,Javascript,Node.js,Mongodb,Express,Functional Programming,有很多关于JavaScript函数式编程理论的好文章。有些甚至包含代码示例,显示命令式/面向对象编程和声明式/函数式编程之间的区别。但我没有发现一个简单的JavaScript代码示例能够说明如何在web应用程序中处理副作用。没有一个真实世界的应用程序能够完全避免副作用(数据库调用、登录到控制台、保存到文件、绘制到屏幕等),我很难弄清楚在实践中是如何做到的 有一些博客文章和S/O答案(像这样:)涉及到在现实世界中处理副作用的主题,但它们通常并不简单,不包括代码示例或其他语言(Haskell、Sca

有很多关于JavaScript函数式编程理论的好文章。有些甚至包含代码示例,显示命令式/面向对象编程和声明式/函数式编程之间的区别。但我没有发现一个简单的JavaScript代码示例能够说明如何在web应用程序中处理副作用。没有一个真实世界的应用程序能够完全避免副作用(数据库调用、登录到控制台、保存到文件、绘制到屏幕等),我很难弄清楚在实践中是如何做到的

有一些博客文章和S/O答案(像这样:)涉及到在现实世界中处理副作用的主题,但它们通常并不简单,不包括代码示例或其他语言(Haskell、Scala等)中的代码示例。我还没有找到Node/JavaScript的

/*app.js*/

const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var greetingSchema = mongoose.Schema({
    greeting: String
});

var Greeting = mongoose.model('Greeting', greetingSchema);

app.get('/', function (req, res) {
  Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
    res.send(greeting);
  });  
});

app.post('/', function (req, res) {
  Greeting.create({greeting: 'Wasssssssssssuuuuppppp'}, function (err, greeting){
  res.send(greeting);
  });      
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})
所以。。。考虑到以下使用MongoDB数据库的非常简单的示例Node/Express应用程序,必须实现哪些代码更改,以使这段代码完全反映当前的JavaScript函数式编程最佳实践。尤其是在处理数据库调用的路由/函数方面。我希望你的回答能帮助我和其他人更好地理解函数式编程的“避免副作用”概念在现实世界JavaScript中的实际应用

/*app.js*/

const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var greetingSchema = mongoose.Schema({
    greeting: String
});

var Greeting = mongoose.model('Greeting', greetingSchema);

app.get('/', function (req, res) {
  Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
    res.send(greeting);
  });  
});

app.post('/', function (req, res) {
  Greeting.create({greeting: 'Wasssssssssssuuuuppppp'}, function (err, greeting){
  res.send(greeting);
  });      
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

您将无法完全避免副作用,但您可以尽可能最大限度地将其消除

例如,Express框架本质上是必需的。您运行诸如
res.send()
之类的函数完全是为了它们的副作用(大多数时候您甚至不关心它的返回值)

除了对所有声明使用
const
,使用数据结构,将所有函数编写为
const-fun=arg=>expression;
而不是
const-fun=(arg)=>{statement;statement;};
等之外,您可以做的是对Express通常的工作方式进行一些抽象

例如,您可以创建以
req
为参数的函数,并返回一个对象,该对象包含响应状态、头和要作为主体进行管道传输的流。从某种意义上说,这些函数可以是纯函数,它们的返回值仅取决于它们的参数(请求对象),但您仍然需要一些包装器来使用Express固有的命令式API实际发送响应。这可能不是小事,但可以做到

作为一个例子,考虑将主体作为对象发送为JSON:

的函数。
const wrap = f => (req, res) => {
  const { status = 200, headers = {}, body = {} } = f(req);
  res.status(status).set(headers).json(body);
};
它可用于创建如下路由处理程序:

app.get('/sum/:x/:y', wrap(req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: +req.params.x + +req.params.y },
})));
使用返回单个表达式且没有副作用的函数

完整示例:

const app = require('express')();

const wrap = f => (req, res) => {
  const { status = 200, headers = {}, body = {} } = f(req);
  res.status(status).set(headers).json(body);
};

app.get('/sum/:x/:y', wrap(req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: +req.params.x + +req.params.y },
})));

app.listen(4444);
测试响应:

$ curl localhost:4444/sum/2/4 -v
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 4444 (#0)
> GET /sum/2/4 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:4444
> Accept: */*
> 
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Foo: Bar
< Content-Type: application/json; charset=utf-8
< Content-Length: 12
< ETag: W/"c-Up02vIPchuYz06aaEYNjufz5tpQ"
< Date: Wed, 19 Jul 2017 15:14:37 GMT
< Connection: keep-alive
< 
* Connection #0 to host localhost left intact
{"result":6}
和一个处理程序:

const delay = (t, v) => new Promise(resolve => setTimeout(() => resolve(v), t));

app.get('/sum/:x/:y', wrap(req =>
  delay(1000, +req.params.x + +req.params.y).then(result => ({
    headers: { 'Foo': 'Bar' },
    body: { result },
  }))));
我在处理程序本身中使用了
.then()
而不是
async
/
wait
,以使其看起来更具功能性,但它可以写成:

app.get('/sum/:x/:y', wrap(async req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: await delay(1000, +req.params.x + +req.params.y) },
})));

如果作为
wrap
参数的函数是一个生成器,而不是只产生解析承诺(像基于生成器的协程通常所做的那样),它将产生解析承诺或流式处理,并使用一些包装来区分这两者,那么它可以变得更加通用。这只是一个基本概念,但可以进一步扩展。

这是一个不适定问题。正如您所说,“一旦完全转换为函数式编程,代码会是什么样子”——您所问的问题——是毫无意义的,因为“任何真实世界的应用程序都无法完全避免副作用”。上面的代码充满了副作用(登录到控制台、发送问候语、连接到数据库)。您已经避免了循环,并使用了.find和lambda函数,因此本质上没有状态的部分已经可以使用了。不清楚你想从答案中得到什么。(我没有否决这个问题,但这可能就是为什么有人这么做的原因)。@anandsun感谢您的反馈。我很乐意修改这个问题,使之更好。也许你能帮我做那件事。我的印象是,我问题中的代码示例没有反映JavaScript函数式编程的最佳实践。我所读到的关于FP的所有内容都让人觉得您必须使用流、单子和所有其他复杂的东西来处理应用程序中的副作用和状态更改。我想知道这在JavaScript中是什么样子的。你是说我提供的代码和JavaScript中FP的代码一样好吗?re:“你必须使用流、单子和所有其他复杂的东西。”Eww,不!这是一种常见的误解,它会让人们远离FP,因为他们认为FP太复杂了。我喜欢FP并在工作中使用Scala,但我不同意必须使用Monad和streams。例如,见。单子>空值。小溪很好。但首先,将状态性推到代码的边缘,将逻辑拆分为小函数,使用标准库函数,避免函数中的副作用,等等。另一个问题是,这样的问题可能在StackExchange或其他方面更成功,因为你问的不是如何让某些东西工作,而是关于风格的建议,这在StackOverflow上通常是不被鼓励的,因为它是主观的。@neoflash本质上意味着你用thunks(没有参数的函数)延迟不纯的计算,围绕它们构建纯粹的函数组合,并让调用方实际执行它们。通过这样做,你推迟了效果,或者更具寓意,你把它推到了应用程序的边缘。这个答案并没有解决我的具体问题,但它得到了一个令人敬畏的答案!我使用这种方法编写API处理程序,为补丁请求提供服务,并将它们转换为相应的mongoose(子)文档更新。所有的处理器都是纯功能的;他们在一个极小的环境下工作