Haskell 对 Node.js 的响应是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3847108/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 13:41:33  来源:igfitidea点击:

What is the Haskell response to Node.js?

multithreadinghaskellconcurrencynode.js

提问by gawi

I believe the Erlang community is not envious of Node.js as it does non-blocking I/O natively and has ways to scale deployments easily to more than one processor (something not even built-in in Node.js). More details at http://journal.dedasys.com/2010/04/29/erlang-vs-node-jsand Node.js or Erlang

我相信 Erlang 社区并不羡慕 Node.js,因为它本机执行非阻塞 I/O,并且可以轻松地将部署扩展到多个处理器(甚至没有内置在 Node.js 中)。更多细节见http://journal.dedasys.com/2010/04/29/erlang-vs-node-jsNode.js 或 Erlang

What about Haskell? Can Haskell provide some of the benefits of Node.js, namely a clean solution to avoid blocking I/O without having recourse to multi-thread programming?

哈斯克尔呢?Haskell 能否提供 Node.js 的一些好处,即一种干净的解决方案,无需求助于多线程编程即可避免阻塞 I/O?



There are many things that are attractive with Node.js

Node.js 有很多吸引人的地方

  1. Events: No thread manipulation, the programmer only provides callbacks (as in Snap framework)
  2. Callbacks are guaranteed to be run in a single thread: no race condition possible.
  3. Nice and simple UNIX-friendly API. Bonus: Excellent HTTP support. DNS also available.
  4. Every I/O is by default asynchronous. This makes it easier to avoid locks. However, too much CPU processing in a callback will impact other connections (in this case, the task should split into smaller sub-tasks and re-scheduled).
  5. Same language for client-side and server-side. (I don't see too much value in this one, however. jQuery and Node.js share the event programming model but the rest is very different. I just can't see how sharing code between server-side and client-side could be useful in practice.)
  6. All this packaged in a single product.
  1. 事件:无线程操作,程序员只提供回调(如在 Snap 框架中)
  2. 回调保证在单个线程中运行:不可能出现竞争条件。
  3. 漂亮而简单的 UNIX 友好 API。奖励:出色的 HTTP 支持。DNS也可用。
  4. 默认情况下,每个 I/O 都是异步的。这使得更容易避免锁定。但是,回调中过多的 CPU 处理会影响其他连接(在这种情况下,任务应该拆分为更小的子任务并重新调度)。
  5. 客户端和服务器端使用相同的语言。(不过,我认为这没有太多价值。jQuery 和 Node.js 共享事件编程模型,但其余部分非常不同。我只是看不出如何在服务器端和客户端之间共享代码在实践中很有用。)
  6. 所有这些都打包在一个产品中。

回答by Simon Marlow

Ok, so having watched a little of the node.js presentationthat @gawi pointed me at, I can say a bit more about how Haskell compares to node.js. In the presentation, Ryan describes some of the benefits of Green Threads, but then goes on to say that he doesn't find the lack of a thread abstraction to be a disadvantage. I'd disagree with his position, particularly in the context of Haskell: I think the abstractions that threads provide are essential for making server code easier to get right, and more robust. In particular:

好的,所以在观看了@gawi 指给我的一些node.js 演示之后,我可以多说一点关于 Haskell 与 node.js 的比较。在演示中,Ryan 描述了 Green Threads 的一些好处,但接着说他不认为缺乏线程抽象是一个缺点。我不同意他的立场,尤其是在 Haskell 的上下文中:我认为线程提供的抽象对于使服务器代码更容易正确和更健壮至关重要。特别是:

  • using one thread per connection lets you write code that expresses the communication with a single client, rather that writing code that deals with allthe clients at the same time. Think of it like this: a server that handles multiple clients with threads looks almost the same as one that handles a single client; the main difference is there's a forksomewhere in the former. If the protocol you're implementing is at all complex, managing the state machine for multiple clients simultaneously gets quite tricky, whereas threads let you just script the communication with a single client. The code is easier to get right, and easier to understand and maintain.

  • callbacks on a single OS thread is cooperative multitasking, as opposed to preemptive multitasking, which is what you get with threads. The main disadvantage with cooperative multitasking is that the programmer is responsible for making sure that there's no starvation. It loses modularity: make a mistake in one place, and it can screw up the whole system. This is really something you don't want to have to worry about, and preemption is the simple solution. Moreover, communication between callbacks isn't possible (it would deadlock).

  • concurrency isn't hard in Haskell, because most code is pure and so is thread-safe by construction. There are simple communication primitives. It's much harder to shoot yourself in the foot with concurrency in Haskell than in a language with unrestricted side effects.

  • 每个连接使用一个线程可以让您编写表达与单个客户端通信的代码,而不是编写同时处理所有客户端的代码。可以这样想:处理多个带有线程的客户端的服务器与处理单个客户端的服务器看起来几乎相同;主要区别在于fork前者有一个地方。如果您正在实现的协议非常复杂,那么同时管理多个客户端的状态机就会变得非常棘手,而线程让您只需编写与单个客户端的通信脚本即可。代码更容易正确,更容易理解和维护。

  • 单个 OS 线程上的回调是协作式多任务处理,而不是抢占式多任务处理,这是您使用线程获得的。协作式多任务处理的主要缺点是程序员负责确保没有饥饿。它失去了模块化:在一个地方犯错,它可能会搞砸整个系统。这确实是您不想担心的事情,抢占是简单的解决方案。此外,回调之间的通信是不可能的(它会死锁)。

  • 并发在 Haskell 中并不难,因为大多数代码都是纯代码,因此在构造上是线程安全的。有简单的通信原语。在 Haskell 中使用并发比在具有不受限制的副作用的语言中更难。

回答by Don Stewart

Can Haskell provide some of the benefits of Node.js, namely a clean solution to avoid blocking I/O without having recourse to multi-thread programming?

Haskell 能否提供 Node.js 的一些好处,即一种干净的解决方案,无需求助于多线程编程即可避免阻塞 I/O?

Yes, in fact events and threads are unified in Haskell.

是的,事实上事件和线程在 Haskell 中是统一的。

  • You can program in explicit lightweight threads (e.g. millions of threads on a single laptop).
  • Or; you can program in an async event-driven style, based on scalable event notification.
  • 您可以在显式轻量级线程中进行编程(例如,单个笔记本电脑上的数百万个线程)。
  • 或者; 您可以基于可扩展的事件通知以异步事件驱动的方式进行编程。

Threads are actually implemented in terms of events, and run across multiple cores, with seamless thread migration, with documented performance, and applications.

线程实际上是在事件方面实现的,并跨多个内核运行,具有无缝的线程迁移、记录的性能和应用程序。

E.g. for

例如对于

Concurrent collections nbody on 32 cores

32 核上的并发集合 nbody

alt text

替代文字

In Haskell you have both events and threads, and as it is all events under the hood.

在 Haskell 中,您有事件和线程,因为它是幕后的所有事件。

Read the paperdescribing the implementation.

阅读描述实现的论文

回答by dan_waterworth

First up, I don't hold your view that node.js is doing the right thing exposing all of those callbacks. You end up writing your program in CPS (continuation passing style) and I think it should be the compiler's job to do that transformation.

首先,我不认为 node.js 正在做正确的事情暴露所有这些回调。您最终以 CPS(连续传递风格)编写程序,我认为编译器的工作应该是进行这种转换。

Events: No thread manipulation, the programmer only provides callbacks (as in Snap framework)

事件:无线程操作,程序员只提供回调(如在 Snap 框架中)

So with this in mind, you can write using a asynchronous style if you so wish, but by doing so you'd miss out on writing in an efficient synchronous style, with one thread per request. Haskell is ludicrously efficient at synchronous code, especially when compared to other languages. It's all events underneath.

因此,考虑到这一点,如果您愿意,您可以使用异步样式进行编写,但是这样做您将错过以高效的同步样式编写,每个请求一个线程。Haskell 在同步代码方面非常高效,尤其是与其他语言相比时。下面是所有事件。

Callbacks are guaranteed to be run in a single thread: no race condition possible.

回调保证在单个线程中运行:不可能出现竞争条件。

You could still have a race condition in node.js, but it's more difficult.

你仍然可以在 node.js 中遇到竞争条件,但它更难。

Every request is in it's own thread. When you write code that has to communicate with other threads, it's very simple to make it threadsafe thanks to haskell's concurrency primitives.

每个请求都在它自己的线程中。当您编写必须与其他线程通信的代码时,由于 haskell 的并发原语,使其成为线程安全的非常简单。

Nice and simple UNIX-friendly API. Bonus: Excellent HTTP support. DNS also available.

漂亮而简单的 UNIX 友好 API。奖励:出色的 HTTP 支持。DNS也可用。

Take a look in hackage and see for yourself.

看看 hackage 并亲眼看看。

Every I/O is by default asynchronous (this can be annoying sometimes, though). This makes it easier to avoid locks. However, too much CPU processing in a callback will impact other connections (in this case, the task should split into smaller sub-tasks and re-scheduled).

默认情况下,每个 I/O 都是异步的(尽管有时这可能很烦人)。这使得更容易避免锁定。但是,回调中过多的 CPU 处理会影响其他连接(在这种情况下,任务应该拆分为更小的子任务并重新调度)。

You have no such problems, ghc will distribute your work amongst real OS threads.

你没有这样的问题,ghc 会在真正的操作系统线程之间分配你的工作。

Same language for client-side and server-side. (I don't see too much value in this one, however. JQuery and Node.js share the event programming model but the rest is very different. I just can't see how sharing code between server-side and client-side could be useful in practice.)

客户端和服务器端使用相同的语言。(不过,我认为这没有太多价值。JQuery 和 Node.js 共享事件编程模型,但其余部分非常不同。我只是看不出如何在服务器端和客户端之间共享代码在实践中很有用。)

Haskell can't possibly win here... right? Think again, http://www.haskell.org/haskellwiki/Haskell_in_web_browser.

Haskell 不可能在这里获胜......对吧?再想一想,http://www.haskell.org/haskellwiki/Haskell_in_web_browser

All this packaged in a single product.

所有这些都打包在一个产品中。

Download ghc, fire up cabal. There's a package for every need.

下载 ghc,启动阴谋集团。有一个包可以满足各种需求。

回答by vlprans

I personally see Node.js and programming with callbacks as unnecessarily low-level and a bit unnatural thing. Why program with callbacks when a good runtime such as the one found in GHC may handle callbacks for you and do so pretty efficiently?

我个人认为 Node.js 和使用回调编程是不必要的低级和有点不自然的事情。当一个良好的运行时(例如 GHC 中的运行时)可以为您处理回调并且这样做非常有效时,为什么要使用回调进行编程?

In the meantime, GHC runtime has improved greatly: it now features a "new new IO manager" called MIOwhere "M" stands for multicore I believe. It builds on foundation of existing IO manager and its main goal is to overcome the cause of 4+ cores performance degradation. Performance numbers provided in this paper are pretty impressive. See yourself:

与此同时,GHC 运行时有了很大的改进:它现在具有一个名为MIO的“新的新 IO 管理器” ,我相信其中“M”代表多核。它建立在现有 IO 管理器的基础上,其主要目标是克服 4+ 核性能下降的原因。本文中提供的性能数据令人印象深刻。看看你自己:

With Mio, realistic HTTP servers in Haskell scale to 20 CPU cores, achieving peak performance up to factor of 6.5x compared to the same servers using previous versions of GHC. The latency of Haskell servers is also improved: [...] under a moderate load, reduces expected response time by 5.7x when compared with previous versions of GHC

使用 Mio,Haskell 中的真实 HTTP 服务器可扩展到 20 个 CPU 内核,与使用先前版本 GHC 的相同服务器相比,峰值性能高达 6.5 倍。Haskell 服务器的延迟也得到了改善:[...] 在中等负载下,与以前版本的 GHC 相比,预期响应时间减少了 5.7 倍

And:

和:

We also show that with Mio, McNettle (an SDN controller written in Haskell) can scale effectively to 40+ cores, reach a thoroughput of over 20 million new requests per second on a single machine, and hence become the fastest of all existing SDN controllers.

我们还表明,使用 Mio,McNettle(用 Haskell 编写的 SDN 控制器)可以有效地扩展到 40 多个内核,在单台机器上达到每秒超​​过 2000 万个新请求的吞吐量,从而成为所有现有 SDN 控制器中最快的.

Mio has made it into GHC 7.8.1 release. I personally see this as a major step forward in Haskell performance. It would be very interesting to compare existing web applications performance compiled by the previous GHC version and 7.8.1.

Mio 已将其纳入 GHC 7.8.1 版本。我个人认为这是 Haskell 性能向前迈出的重要一步。比较由以前的 GHC 版本和 7.8.1 编译的现有 Web 应用程序性能会非常有趣。

回答by agocorona

IMHO events are good, but programming by means of callbacks is not.

恕我直言事件是好的,但通过回调编程不是。

Most of the problems that makes special the coding and debugging of web applications comes from what makes them scalable and ?exible. The most important, the stateless nature of HTTP. This enhances navigability, but this imposes an inversion of control where the IO element (the web server in this case) call different handlers in the application code. This event model -or callback model, more accurately said- is a nightmare, since callbacks do not share variable scopes, and an intuitive view of the navigation is lost. It is very difficult to prevent all the possible state changes when the user navigate back and forth, among other problems.

大多数使 Web 应用程序的编码和调试变得特别的问题来自于使它们具有可扩展性和灵活性的原因。最重要的是,HTTP 的无状态特性。这增强了可导航性,但这会导致控制反转,其中 IO 元素(在本例中为 Web 服务器)在应用程序代码中调用不同的处理程序。这个事件模型 - 或者回调模型,更准确地说 - 是一场噩梦,因为回调不共享变量范围,并且导航的直观视图丢失了。当用户来回导航以及其他问题时,很难阻止所有可能的状态更改。

It may be said that the problems are similar to GUI programming where the event model works fine, but GUIs have no navigation and no back button. That multiplies the state transitions possible in web applications. The result of the attempt to solve these problem are heavy frameworks with complicated configurations plenty of pervasive magic identifiers without questioning the root of the problem: the callback model and its inherent lack of sharing of variable scopes, and no sequencing, so the sequence has to be constructed by linking identifiers.

可以说这些问题类似于 GUI 编程,其中事件模型工作正常,但 GUI 没有导航和后退按钮。这增加了 Web 应用程序中可能的状态转换。尝试解决这些问题的结果是配置复杂的重型框架大量无处不在的魔法标识符而不质疑问题的根源:回调模型及其固有的缺乏共享变量范围,并且没有排序,因此序列必须通过链接标识符来构造。

There are sequential based frameworks like ocsigen (ocaml) seaside (smalltalk) WASH (discontinued, Haskell) and mflow (Haskell) that solve the problem of state management while maintaining navigability and REST-fulness. within these frameworks, the programmer can express the navigation as a imperative sequence where the program send pages and wait for responses in a single thread, variables are in scope and the back button works automatically. This inherently produces shorter, more safe, more readable code where the navigation is clearly visible to the programmer. (fair warning: I′m the developer of mflow)

有一些基于顺序的框架,如 ocsigen (ocaml) seaside (smalltalk) WASH (discontinued, Haskell) 和 mflow (Haskell),它们解决了状态管理的问题,同时保持了可导航性和 REST 的完整性。在这些框架中,程序员可以将导航表示为命令式序列,其中程序在单个线程中发送页面并等待响应,变量在范围内,后退按钮自动工作。这本质上会产生更短、更安全、更易读的代码,其中导航对程序员来说是清晰可见的。(公平警告:我是 mflow 的开发者)

回答by Greg Weber

The question is pretty ridiculous because 1) Haskell has already solved this issue in a much better way and 2) in roughly the same way Erlang has. Here is the benchmark against node: http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks

这个问题非常荒谬,因为 1) Haskell 已经以更好的方式解决了这个问题,并且 2) 以与 Erlang 大致相同的方式解决了这个问题。这是针对节点的基准:http: //www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks

Give Haskell 4 cores and it can do 100k (simple) requests per second in a single application. Node can't do as many, and can't scale a single application across cores. And you don't have to do anything to reap this because the Haskell runtime is non-blocking. The only other (relatively common) language that has non-blocking IO built into the runtime is Erlang.

给 Haskell 4 个内核,它可以在单个应用程序中每秒处理 100k(简单)请求。Node 不能做那么多,也不能跨内核扩展单个应用程序。而且你不需要做任何事情来获得它,因为 Haskell 运行时是非阻塞的。运行时中内置非阻塞 IO 的唯一其他(相对常见)语言是 Erlang。