2014-09-25 9 views
8

Ich bin neu in Clojure core.async-Bibliothek, und ich versuche es durch das Experiment zu verstehen.konnte For-Schleife in Go-Block von core.async nicht verwenden?

Aber als ich versuchte:

(let [i (async/chan)] (async/go (doall (for [r [1 2 3]] (async/>! i r))))) 

es gibt mir eine sehr seltsame Ausnahme:

CompilerException java.lang.IllegalArgumentException: No method in multimethod '-item-to-ssa' for dispatch value: :fn 

und ich versuchte, einen anderen Code:

(let [i (async/chan)] (async/go (doseq [r [1 2 3]] (async/>! i r)))) 

es keine Compiler Ausnahme haben an alle.

Ich bin total verwirrt. Was ist passiert?

+1

'' für'' in clojure ist * nicht * eine Schleife - es ist ein Listenverständnis. In diesem Sinne ist Ihr '' '' - Beispiel keine idiomatische Methode, um die Nebeneffekte '>!'Funktion. Vielleicht könnte/sollte die Compiler-Nachricht verbessert werden, aber Ihr grundsätzliches Problem ist, dass die Verwendung von '' '' auf diese Weise keinen Sinn macht. Das '' doseq'' ist völlig in Ordnung. – sw1nn

+0

>! Blöcke, es wartet auf jemanden aus dem Kanal zu lesen. Versuchen Sie zuerst, den Leseteil einzurichten, oder verwenden Sie put! – edbond

+0

@edbond wow, das funktioniert eigentlich .. aber ich bin noch verwirrter, Timothy Baldridge sagte gerade nicht async/Go konnte nicht mit 'fn' in' async/go' Block umgehen? – xudifsd

Antwort

16

Also stoppt der Clojure Go-Block die Translation an Funktionsgrenzen aus vielen Gründen, aber die größte ist die Einfachheit. Dies wird am häufigsten gesehen, wenn ein faulen seq Konstruktion:

(go (lazy-seq (<! c))) 

zusammengestellt erhält in etwa wie folgt:

(go (clojure.lang.LazySeq. (fn [] (<! c)))) 

Jetzt ist über diese schnelle lassen denkt ... was soll diese Rückkehr? Vorausgesetzt, was Sie wahrscheinlich wollten, war ein Lazy Seq, der den Wert aus c enthält, aber der <! muss den verbleibenden Code der Funktion in einen Callback übersetzen, aber LazySeq erwartet, dass die Funktion synchron ist. Um diese Einschränkung herum gibt es keinen Weg.

auf Ihre Frage Also zurück, wenn Sie for macroexpand Sie werden sehen, dass es nicht wirklich Schleife tut, sondern es dehnt sich in einen Haufen von Code, der schließlich lazy-seq Anrufe und so Parkplatz ops im Inneren des Körpers nicht funktionieren. doseq (und dotimes) werden jedoch von loop/recur unterstützt und so werden diese vollkommen in Ordnung funktionieren.

Es gibt ein paar andere Orte, an denen Sie stolpern könnten with-bindings ist ein Beispiel. Grundsätzlich, wenn ein Makro Ihre core.async Park-Operationen in eine verschachtelte Funktion hält, erhalten Sie diesen Fehler.

Mein Vorschlag ist dann, den Körper Ihrer Go-Blöcke so einfach wie möglich zu halten. Schreibe reine Funktionen und behandle dann den Körper von go-Blöcken als Orte, an denen IO ausgeführt werden soll.

------------ ------------- EDIT

Durch Übersetzung Stops an Funktion Grenzen, meine ich dies: die Go-Block hat seinen Körper und übersetzt es in eine Zustandsmaschine. Jeder Aufruf an <!>! oder alts! (und einige andere) gelten als Zustandsmaschine Übergänge, wo die Ausführung des Blocks anhalten kann. An jedem dieser Punkte wird die Maschine in einen Rückruf umgewandelt und an den Kanal angeschlossen. Wenn dieses Makro eine fn Form erreicht, hört es auf zu übersetzen. Sie können also nur innerhalb eines Go-Blocks von <! aus anrufen, nicht innerhalb einer Funktion innerhalb eines Codeblocks.

Dies ist Teil der Magie von core.async. Ohne das go-Makro würde core.async-Code in anderen Sprachen wie Rückruf-Hölle aussehen.

+1

Würdest du mir bitte erklären, was du unter "Stoppt die Übersetzung an Funktionsgrenzen" meinst? – Chiron

+1

zu meinem ursprünglichen Beitrag hinzugefügt –

+0

Ich sehe, so vielleicht core.async könnte eine weitere Methode hinzufügen, um: Fn und eine bessere Ausnahme zu werfen. Es ist sehr Anfänger unfreundlich. – xudifsd