Wie lautet die Antwort von Haskell auf Node.js?


217

Ich glaube, die Erlang-Community ist nicht neidisch auf Node.js, da sie nicht blockierende E / A nativ ausführt und Möglichkeiten bietet, Bereitstellungen einfach auf mehr als einen Prozessor zu skalieren (etwas, das nicht einmal in Node.js integriert ist). Weitere Informationen finden Sie unter http://journal.dedasys.com/2010/04/29/erlang-vs-node-js und Node.js oder Erlang

Was ist mit Haskell? Kann Haskell einige der Vorteile von Node.js bieten, nämlich eine saubere Lösung, um das Blockieren von E / A zu vermeiden, ohne auf Multithread-Programmierung zurückgreifen zu müssen?


Es gibt viele Dinge, die mit Node.js attraktiv sind

  1. Ereignisse: Keine Thread-Manipulation, der Programmierer bietet nur Rückrufe an (wie im Snap-Framework).
  2. Rückrufe werden garantiert in einem einzigen Thread ausgeführt: Keine Racebedingung möglich.
  3. Schöne und einfache UNIX-freundliche API. Bonus: Hervorragende HTTP-Unterstützung. DNS ebenfalls verfügbar.
  4. Jede E / A ist standardmäßig asynchron. Dies erleichtert das Vermeiden von Sperren. Zu viel CPU-Verarbeitung in einem Rückruf wirkt sich jedoch auf andere Verbindungen aus (in diesem Fall sollte die Aufgabe in kleinere Unteraufgaben aufgeteilt und neu geplant werden).
  5. Gleiche Sprache für clientseitig und serverseitig. (Ich sehe jedoch nicht zu viel Wert in diesem. JQuery und Node.js teilen sich das Ereignisprogrammierungsmodell, aber der Rest ist sehr unterschiedlich. Ich kann nur nicht sehen, wie das Teilen von Code zwischen Server- und Client-Seite möglich ist in der Praxis nützlich sein.)
  6. All dies in einem einzigen Produkt verpackt.

17
Ich denke, Sie sollten diese Frage stattdessen Programmierern stellen .
Jonas

47
Das Fehlen eines Codes macht es nicht zu einer subjektiven Frage.
Gawi

20
Ich weiß nicht viel über node.js, aber eines ist mir bei Ihrer Frage aufgefallen: Warum finden Sie die Aussicht auf Threads so unangenehm? Threads sollten genau die richtige Lösung für das Multiplexen von E / A sein. Ich verwende den Begriff Threads hier allgemein, einschließlich der Prozesse von Erlang. Vielleicht machst du dir Sorgen um Schlösser und veränderlichen Zustand? Sie müssen die Dinge nicht so machen - verwenden Sie Nachrichtenübermittlung oder Transaktionen, wenn dies für Ihre Anwendung sinnvoller ist.
Simon Marlow

9
@gawi Ich denke nicht, dass das sehr einfach zu programmieren klingt - ohne Vorbehalt muss man sich mit der Möglichkeit von Hunger und langen Latenzen auseinandersetzen. Grundsätzlich sind Threads die richtige Abstraktion für einen Webserver - es ist nicht erforderlich, sich mit asynchronen E / A und all den damit verbundenen Schwierigkeiten zu befassen. Tun Sie dies einfach in einem Thread. Übrigens habe ich einen Artikel
Simon Marlow

3
"Rückrufe werden garantiert in einem einzigen Thread ausgeführt: Keine Racebedingung möglich." Falsch. Sie können leicht Rennbedingungen in Node.js haben; Nehmen Sie einfach an, dass eine E / A-Aktion vor einer anderen abgeschlossen wird, und BOOM. Was in der Tat unmöglich ist, ist eine bestimmte Art von Race-Bedingungen, nämlich der gleichzeitige nicht synchronisierte Zugriff auf dasselbe Byte im Speicher.
Rechtsfalte

Antworten:


219

Ok, nachdem ich ein wenig von der Präsentation von node.js gesehen habe , auf die @gawi mich hingewiesen hat, kann ich etwas mehr darüber sagen, wie Haskell mit node.js verglichen wird. In der Präsentation beschreibt Ryan einige der Vorteile von Green Threads, sagt dann aber, dass er das Fehlen einer Thread-Abstraktion nicht als Nachteil empfindet. Ich würde seiner Position nicht zustimmen, insbesondere im Zusammenhang mit Haskell: Ich denke, die Abstraktionen, die Threads bereitstellen, sind wesentlich, damit Servercode leichter richtig und robuster wird. Bestimmtes:

  • Wenn Sie einen Thread pro Verbindung verwenden, können Sie Code schreiben, der die Kommunikation mit einem einzelnen Client ausdrückt, anstatt Code zu schreiben, der alle Clients gleichzeitig behandelt. Stellen Sie sich das so vor: Ein Server, der mehrere Clients mit Threads verarbeitet, sieht fast genauso aus wie einer, der einen einzelnen Client verarbeitet. Der Hauptunterschied ist, dass es forkirgendwo im ersteren einen gibt. Wenn das von Ihnen implementierte Protokoll überhaupt komplex ist, wird die Verwaltung der Zustandsmaschine für mehrere Clients gleichzeitig ziemlich schwierig, während Sie in Threads nur die Kommunikation mit einem einzelnen Client per Skript ausführen können. Der Code ist leichter richtig zu machen und leichter zu verstehen und zu pflegen.

  • Rückrufe auf einem einzelnen Betriebssystem-Thread sind kooperatives Multitasking im Gegensatz zu präemptivem Multitasking, das Sie mit Threads erhalten. Der Hauptnachteil beim kooperativen Multitasking besteht darin, dass der Programmierer dafür verantwortlich ist, dass kein Hunger auftritt. Es verliert an Modularität: Machen Sie einen Fehler an einer Stelle und es kann das gesamte System vermasseln. Dies ist wirklich etwas, worüber Sie sich keine Sorgen machen müssen, und Preemption ist die einfache Lösung. Darüber hinaus ist eine Kommunikation zwischen Rückrufen nicht möglich (dies würde zum Stillstand führen).

  • Parallelität ist in Haskell nicht schwer, da der meiste Code rein und daher von Natur aus threadsicher ist. Es gibt einfache Kommunikationsprimitive. In Haskell ist es viel schwieriger, sich mit Parallelität in den Fuß zu schießen, als in einer Sprache mit uneingeschränkten Nebenwirkungen.


42
Ok, ich verstehe, dass node.js eine Lösung für zwei Probleme ist: 1. Parallelität ist in den meisten Sprachen schwierig, 2. Verwenden von Betriebssystem-Threads ist expansiv. Die Lösung von Node.j besteht darin, die ereignisbasierte Parallelität (w / libev) zu verwenden, um die Kommunikation zwischen Threads und Skalierbarkeitsprobleme von Betriebssystem-Threads zu vermeiden. Haskell hat wegen der Reinheit kein Problem Nr. 1. Für # 2 verfügt Haskell über Lightweight-Threads + Event-Manager, der kürzlich in GHC für große Kontexte optimiert wurde. Die Verwendung von Javascript kann für keinen Haskell-Entwickler als Plus angesehen werden. Für einige Leute, die das Snap Framework verwenden, ist Node.js "nur schlecht".
Gawi

4
Die Anforderungsverarbeitung ist meistens eine Folge von voneinander abhängigen Operationen. Ich stimme eher zu, dass die Verwendung von Rückrufen für jede Blockierungsoperation umständlich sein kann. Threads sind dafür besser geeignet als Callback.
Gawi

10
Ja! Das brandneue E / A-Multiplexing in GHC 7 macht das Schreiben von Servern in Haskell noch besser.
andreypopp

3
Ihr erster Punkt macht für mich (als Außenseiter) wenig Sinn ... Wenn Sie eine Anfrage in node.js bearbeiten, handelt Ihr Rückruf von einem einzelnen Client. Das Verwalten des Status ist nur dann ein Problem, wenn Sie auf mehrere Prozesse skalieren, und selbst dann ist es mit den verfügbaren Bibliotheken recht einfach.
Ricardo Tomasi

12
Es ist kein separates Thema. Wenn es sich bei dieser Frage um eine echte Suche nach den besten Werkzeugen für den Job in Haskell handelt oder um eine Überprüfung, ob in Haskell hervorragende Werkzeuge für den Job vorhanden sind, muss die implizite Annahme, dass Multithread-Programmierung ungeeignet wäre, in Frage gestellt werden, da dies bei Haskell der Fall ist Fäden eher anders, wie Don Stewart betont. Antworten, die erklären, warum die Haskell-Community auch nicht eifersüchtig auf Node.js ist, sind für diese Frage sehr thematisch. Gawis Antwort deutet darauf hin, dass es eine angemessene Antwort auf seine Frage war.
AndrewC

154

Kann Haskell einige der Vorteile von Node.js bieten, nämlich eine saubere Lösung, um das Blockieren von E / A zu vermeiden, ohne auf Multithread-Programmierung zurückgreifen zu müssen?

Ja, tatsächlich sind Ereignisse und Threads in Haskell vereinheitlicht.

  • Sie können explizite Lightweight-Threads programmieren (z. B. Millionen von Threads auf einem einzelnen Laptop).
  • Oder; Sie können in einem asynchronen ereignisgesteuerten Stil programmieren, der auf einer skalierbaren Ereignisbenachrichtigung basiert.

Threads werden tatsächlich in Bezug auf Ereignisse implementiert und über mehrere Kerne mit nahtloser Thread-Migration, dokumentierter Leistung und Anwendungen ausgeführt.

ZB für

Gleichzeitige Sammlungen nbody auf 32 Kernen

Alt-Text

In Haskell haben Sie sowohl Ereignisse als auch Themen, und wie es alle Ereignisse unter der Haube sind.

Lesen Sie das Dokument, in dem die Implementierung beschrieben wird.


2
Vielen Dank. Ich muss das alles verdauen ... Dies scheint GHC-spezifisch zu sein. Ich denke es ist OK. Die Haskell-Sprache ist manchmal so, wie alles, was GHC kompilieren kann. In ähnlicher Weise ist die "Plattform" von Haskell mehr oder weniger die GHC-Laufzeit.
Gawi

1
@gawi: Das und alle anderen Pakete, die direkt darin gebündelt werden, so dass es sofort nützlich ist. Und das ist das gleiche Bild, das ich in meinem CS-Kurs gesehen habe. und das Beste daran ist, dass es in Haskell nicht schwer ist, in Ihren eigenen Programmen ähnlich beeindruckende Ergebnisse zu erzielen.
Robert Massaioli

1
Hallo Don, denkst du, du könntest einen Link zu dem Haskell-Webserver erstellen, der bei der Beantwortung solcher Fragen die beste Leistung (Warp) erbringt? Hier ist der ziemlich relevante Benchmark gegen Node.js: yesodweb.com/blog/2011/03/…
Greg Weber

4
Nur theoretisch. Haskell "leichte Fäden" sind nicht so leicht wie Sie denken. Es ist viel, viel, viel billiger, einen Rückruf auf einer Epoll-Schnittstelle zu registrieren, als einen sogenannten grünen Thread zu planen. Sie sind natürlich billiger als OS-Threads, aber sie sind nicht kostenlos. Für die Erstellung von 100.000 davon werden ca. 350 MB Speicher und nehmen Sie sich etwas Zeit. Versuchen Sie 100.000 Verbindungen mit node.js. Überhaupt kein Problem . Es wäre magisch, wenn es nicht schneller wäre, da ghc epoll unter der Haube verwendet, so dass sie nicht schneller sein können als epoll direkt zu verwenden. Die Programmierung mit Thread-Schnittstelle ist allerdings recht nett.
Kr0e

3
Zusätzlich: Der neue E / A-Manager (ghc) verwendet einen Planungsalgorithmus mit (m log n) Komplexität (wobei m die Anzahl der ausführbaren Threads und n die Gesamtzahl der Threads ist). Epoll hat die Komplexität k (k ist die Anzahl der lesbaren / beschreibbaren fd's =. Ghc hat also O (k * m log n) über die gesamte Komplexität, was nicht sehr gut ist, wenn Sie mit Verbindungen mit hohem Datenverkehr konfrontiert sind. Node.js hat nur die verursachte lineare Komplexität von epoll. Und lassen Sie uns nicht über die Windows-Leistung sprechen ... Node.js ist viel schneller, weil es IOCP verwendet.
Kr0e

20

Erstens bin ich nicht der Ansicht, dass node.js das Richtige tut, um all diese Rückrufe aufzudecken. Am Ende schreiben Sie Ihr Programm in CPS (Continuation Passing Style), und ich denke, es sollte die Aufgabe des Compilers sein, diese Transformation durchzuführen.

Ereignisse: Keine Thread-Manipulation, der Programmierer bietet nur Rückrufe an (wie im Snap-Framework).

In diesem Sinne können Sie auf Wunsch mit einem asynchronen Stil schreiben. Auf diese Weise würden Sie jedoch das Schreiben in einem effizienten synchronen Stil mit einem Thread pro Anforderung verpassen. Haskell ist lächerlich effizient im synchronen Code, insbesondere im Vergleich zu anderen Sprachen. Es sind alles Ereignisse darunter.

Rückrufe werden garantiert in einem einzigen Thread ausgeführt: Keine Racebedingung möglich.

Sie könnten immer noch eine Race-Bedingung in node.js haben, aber es ist schwieriger.

Jede Anfrage ist in einem eigenen Thread. Wenn Sie Code schreiben, der mit anderen Threads kommunizieren muss, ist es dank der Parallelitätsprimitiven von haskell sehr einfach, ihn threadsicher zu machen.

Schöne und einfache UNIX-freundliche API. Bonus: Hervorragende HTTP-Unterstützung. DNS ebenfalls verfügbar.

Werfen Sie einen Blick in Hackage und überzeugen Sie sich selbst.

Jede E / A ist standardmäßig asynchron (dies kann jedoch manchmal ärgerlich sein). Dies erleichtert das Vermeiden von Sperren. Zu viel CPU-Verarbeitung in einem Rückruf wirkt sich jedoch auf andere Verbindungen aus (in diesem Fall sollte die Aufgabe in kleinere Unteraufgaben aufgeteilt und neu geplant werden).

Sie haben keine derartigen Probleme, ghc wird Ihre Arbeit auf echte Betriebssystem-Threads verteilen.

Gleiche Sprache für clientseitig und serverseitig. (Ich sehe jedoch nicht zu viel Wert in diesem. JQuery und Node.js teilen sich das Ereignisprogrammierungsmodell, aber der Rest ist sehr unterschiedlich. Ich kann einfach nicht sehen, wie das Teilen von Code zwischen Server- und Client-Seite möglich ist in der Praxis nützlich sein.)

Haskell kann hier unmöglich gewinnen ... richtig? Denken Sie noch einmal darüber nach, http://www.haskell.org/haskellwiki/Haskell_in_web_browser .

All dies in einem einzigen Produkt verpackt.

Laden Sie ghc herunter, starten Sie die Kabale. Für jeden Bedarf gibt es ein Paket.


Ich habe nur den Anwalt des Teufels gespielt. Also ja, ich stimme Ihren Punkten zu. Mit Ausnahme der clientseitigen und serverseitigen Sprachvereinigung. Obwohl ich denke, dass dies technisch machbar ist, glaube ich nicht, dass es letztendlich das gesamte heutige Javascript-Ökosystem (JQuery und Freunde) ersetzen könnte. Obwohl es sich um ein Argument handelt, das von Node.js Unterstützern vorgebracht wird, denke ich nicht, dass es ein sehr wichtiges ist. Müssen Sie wirklich so viel Code zwischen Ihrer Präsentationsebene und Ihrem Backend teilen? Wollen wir wirklich, dass Programmierer nur eine Sprache beherrschen?
Gawi

Der eigentliche Vorteil besteht darin, dass Sie Seiten sowohl auf Server- als auch auf Clientseite rendern können, um das Erstellen von Echtzeitseiten zu vereinfachen.
dan_waterworth

@dan_waterworth genau finden Meteor oder derby.js
MB21

1
@gawi Wir haben Produktionsservices, bei denen 85% des Codes zwischen Client und Server geteilt werden. Dies wird in der Community als universelles JavaScript bezeichnet. Wir verwenden React, um Inhalte auf dem Server dynamisch zu rendern und die Zeit bis zum ersten nützlichen Rendern im Client zu verkürzen. Obwohl mir bekannt ist, dass Sie Haskell im Browser ausführen können, sind mir keine Best Practices für "universelles Haskell" bekannt, die das serverseitige und clientseitige Rendern mit derselben Codebasis ermöglichen.
Eric Elliott

8

Ich persönlich sehe Node.js und das Programmieren mit Rückrufen als unnötig niedrig und etwas unnatürlich an. Warum mit Rückrufen programmieren, wenn eine gute Laufzeit wie die in GHC Rückrufe für Sie verarbeiten kann und dies ziemlich effizient?

In der Zwischenzeit hat sich die GHC-Laufzeit erheblich verbessert: Es gibt jetzt einen "neuen neuen E / A-Manager" namens MIO, wobei "M" für Multicore steht, glaube ich. Es baut auf der Grundlage des vorhandenen E / A-Managers auf und hat das Hauptziel, die Ursache für Leistungseinbußen bei mehr als 4 Kernen zu überwinden. Die in diesem Dokument angegebenen Leistungszahlen sind ziemlich beeindruckend. Sehen Sie selbst:

Mit Mio lassen sich realistische HTTP-Server in Haskell auf 20 CPU-Kerne skalieren und erreichen eine Spitzenleistung von bis zu dem Faktor 6,5 im Vergleich zu denselben Servern, die frühere Versionen von GHC verwenden. Die Latenz von Haskell-Servern wird ebenfalls verbessert: [...] reduziert bei mäßiger Last die erwartete Antwortzeit im Vergleich zu früheren Versionen von GHC um das 5,7-fache

Und:

Wir zeigen auch, dass McNettle (ein in Haskell geschriebener SDN-Controller) mit Mio effektiv auf über 40 Kerne skaliert werden kann, auf einer einzelnen Maschine einen Durchsatz von über 20 Millionen neuen Anforderungen pro Sekunde erreicht und somit der schnellste aller vorhandenen SDN-Controller wird .

Mio hat es in die GHC 7.8.1-Version geschafft. Ich persönlich sehe dies als einen großen Fortschritt in der Leistung von Haskell. Es wäre sehr interessant, die Leistung bestehender Webanwendungen zu vergleichen, die mit der vorherigen GHC-Version und 7.8.1 kompiliert wurden.


6

IMHO-Ereignisse sind gut, die Programmierung mittels Rückrufen jedoch nicht.

Die meisten Probleme, die das Codieren und Debuggen von Webanwendungen besonders machen, sind darauf zurückzuführen, dass sie skalierbar und flexibel sind. Das wichtigste ist die Staatenlosigkeit von HTTP. Dies verbessert die Navigationsfähigkeit, führt jedoch zu einer Umkehrung der Steuerung, wenn das E / A-Element (in diesem Fall der Webserver) verschiedene Handler im Anwendungscode aufruft. Dieses Ereignismodell - oder genauer gesagt das Rückrufmodell - ist ein Albtraum, da Rückrufe keine variablen Bereiche gemeinsam haben und eine intuitive Ansicht der Navigation verloren geht. Es ist unter anderem sehr schwierig, alle möglichen Statusänderungen zu verhindern, wenn der Benutzer hin und her navigiert.

Es kann gesagt werden, dass die Probleme der GUI-Programmierung ähnlich sind, bei der das Ereignismodell gut funktioniert, GUIs jedoch keine Navigation und keine Zurück-Schaltfläche haben. Das multipliziert die in Webanwendungen möglichen Zustandsübergänge. Das Ergebnis des Versuchs, dieses Problem zu lösen, sind schwere Frameworks mit komplizierten Konfigurationen, die viele allgegenwärtige magische Identifikatoren enthalten, ohne die Wurzel des Problems in Frage zu stellen: das Rückrufmodell und das inhärente Fehlen einer gemeinsamen Nutzung variabler Bereiche und keine Sequenzierung, daher muss die Sequenz durch Verknüpfen von Bezeichnern konstruiert werden.

Es gibt sequentiell basierte Frameworks wie ocsigen (ocaml) Seaside (Smalltalk) WASH (eingestellt, Haskell) und mflow (Haskell), die das Problem des Zustandsmanagements lösen und gleichzeitig die Navigierbarkeit und REST-Fülle beibehalten. Innerhalb dieser Frameworks kann der Programmierer die Navigation als eine zwingende Sequenz ausdrücken, in der das Programm Seiten sendet und auf Antworten in einem einzelnen Thread wartet, Variablen im Gültigkeitsbereich sind und die Zurück-Schaltfläche automatisch funktioniert. Dies erzeugt von Natur aus kürzeren, sichereren und besser lesbaren Code, bei dem die Navigation für den Programmierer klar sichtbar ist. (faire Warnung: Ich bin der Entwickler von mflow)


In node.js werden Rückrufe zur Verarbeitung asynchroner E / A verwendet, z. B. für Datenbanken. Sie sprechen von etwas anderem, das zwar interessant ist, aber die Frage nicht beantwortet.
Robin Green

Du hast recht. Es dauerte drei Jahre, um eine Antwort zu erhalten, die hoffentlich Ihren Einwänden entspricht: github.com/transient-haskell
agocorona

Der Knoten unterstützt jetzt asynchrone Funktionen, dh Sie können Code im imperativen Stil schreiben, der tatsächlich asynchron ist. Es verwendet Versprechen unter der Haube.
Eric Elliott

5

Die Frage ist ziemlich lächerlich, weil 1) Haskell dieses Problem bereits viel besser gelöst hat und 2) Erlang ungefähr auf die gleiche Weise wie Erlang. Hier ist der Benchmark gegen den Knoten: http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks

Geben Sie Haskell 4 Kerne und es kann 100.000 (einfache) Anforderungen pro Sekunde in einer einzigen Anwendung ausführen. Der Knoten kann nicht so viele Anwendungen ausführen und eine einzelne Anwendung nicht über mehrere Kerne hinweg skalieren. Und Sie müssen nichts tun, um dies zu ernten, da die Haskell-Laufzeit nicht blockiert. Die einzige andere (relativ häufige) Sprache, in die nicht blockierende E / A in die Laufzeit integriert sind, ist Erlang.


14
Lächerlich? Die Frage lautet nicht "Hat Haskell eine Antwort?", Sondern "Was ist die Antwort von Haskell?". Zu dem Zeitpunkt, als die Frage gestellt wurde, wurde GHC 7 noch nicht einmal veröffentlicht, sodass Haskell noch nicht "im Spiel" war (außer vielleicht für Frameworks, die libev wie Snap verwenden). Davon abgesehen stimme ich zu.
Gawi

1
Ich weiß nicht, ob dies der Fall war, als Sie diese Antwort gepostet haben, aber jetzt gibt es tatsächlich Knotenmodule, mit denen Knoten-Apps problemlos über Kerne hinweg skaliert werden können. Außerdem vergleicht dieser Link node.js, die auf einem einzelnen Kern ausgeführt werden, mit haskell, das auf 4 Kernen ausgeführt wird. Ich würde es gerne wieder in einer faireren Konfiguration sehen, aber leider ist das Github-Repo weg.
Tim Gautier

2
Haskell mit mehr als 4 Kernen beeinträchtigt die Leistung der Anwendung. Es gab ein Papier zu diesem Thema, an dem aktiv gearbeitet wird, aber es ist immer noch ein Thema. Das Ausführen von 16 Instanzen von Node.js auf 16 Core-Servern ist daher höchstwahrscheinlich viel besser als eine einzelne ghc-Anwendung mit + RTS -N16, die aufgrund dieses Laufzeitfehlers tatsächlich langsamer als + RTS -N1 ist. Dies liegt daran, dass sie nur einen IOManager verwenden, der bei Verwendung mit vielen Betriebssystem-Threads langsamer wird. Ich hoffe, sie werden diesen Fehler beheben, aber er existiert seitdem, also hätte ich nicht viel Hoffnung ...
Kr0e

Jeder, der sich diese Antwort ansieht, sollte sich darüber im Klaren sein, dass Node problemlos 100.000 einfache Anforderungen auf einem einzelnen Kern verarbeiten kann und es trivial einfach ist, eine zustandslose Node-Anwendung über viele Kerne hinweg zu skalieren. pm2 -i max path/to/app.jswird automatisch auf die optimale Anzahl von Instanzen skaliert, basierend auf den verfügbaren Kernen. Darüber hinaus blockiert Node standardmäßig nicht.
Eric Elliott

1

1
Wie beantwortet dies die Frage?
Feuer

1
@dfeuer Der Link muss lauten: Snap Haskell Web Framework hat libev gelöscht. Ich weiß nicht, warum die Formatierung fehlschlägt. Die Laufzeit des Knotenservers drehte sich zu Beginn ausschließlich um Linux libev, ebenso wie Snap Web FrameWork. Haskell mit Snap ist wie ECMAscript mit Nodejs. Daher ist die Entwicklung von Snap zusammen mit Nodejs relevanter als Haskell, was in diesem Zusammenhang besser mit ECMAscript verglichen werden kann.
Chawathe Vipul S
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.