2014-09-25 13 views
8

Jestem nowy w bibliotece clojure core.async i próbuję to zrozumieć poprzez eksperyment.nie można użyć do pętli w go bloku core.async?

Ale kiedy próbowałem:

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

daje mi bardzo dziwny wyjątek:

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

i próbowałem inny kod:

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

to żadnego wyjątku kompilator wszystko.

Jestem całkowicie zdezorientowany. Co się stało?

+1

'' dla '' w clojure to * nie * pętla - jest to zrozumienie listy. Mając to na uwadze, Twój przykład '' for'' nie jest idiomatycznym sposobem wywoływania strony skutkującej '>!'funkcja. Być może komunikat kompilatora mógłby/powinien zostać poprawiony, ale twoim podstawowym problemem jest to, że używanie '' for'' w ten sposób nie ma sensu (clojure). '' Doseq'' jest idealnie w porządku. – sw1nn

+0

>! blokuje, czeka na kogoś, kto odczyta z kanału. Spróbuj najpierw ustawić czytanie, lub użyj put! – edbond

+0

@edbond wow, to faktycznie działa .. ale jestem jeszcze bardziej zdezorientowany, czy Timothy Baldridge nie powiedział "async/go"? – xudifsd

Odpowiedz

16

Tak więc blok Clojure zatrzymuje translację na granicach funkcji, z wielu powodów, ale największą jest prostota. Najczęściej jest to widoczne przy konstruowaniu leniwe SEQ:

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

kompilowany na coś takiego:

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

Teraz pomyślmy o tym bardzo szybko ... co to powinno zamian? Zakładając, że prawdopodobnie chciałeś być leniwym seq zawierającym wartość zaczerpniętą z c, ale <! musi przetłumaczyć pozostały kod funkcji na wywołanie zwrotne, ale LazySeq oczekuje, że funkcja będzie synchroniczna. Naprawdę nie ma sposobu obejścia tego ograniczenia.

Wracając do pytania, jeśli makroexexpand for okaże się, że w rzeczywistości nie jest zapętlone, zamiast tego rozwija się w garść kodu, który ostatecznie wywołuje lazy-seq, więc operacje parkowania nie działają w ciele. doseq (i dotimes) są jednak wspierane przez loop/recur, a więc te będą działać idealnie dobrze.

Jest kilka innych miejsc, w których może to być jeden z przykładów: with-bindings. Zasadniczo, jeśli makro wpiszesz operacje parkowania core.async do funkcji zagnieżdżonej, otrzymasz ten błąd.

Moja sugestia to utrzymywanie ciała z twoich bloków tak proste, jak to tylko możliwe. Napisz czyste funkcje, a następnie traktuj ciało bloków go jako miejsca do wykonania IO.

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

przetłumaczona zatrzymuje się na granicach funkcji, mam na myśli to: blok przejdź czerpie ciało i przekłada je na maszynę stanu. Każde wywołanie do <!>! lub alts! (i kilka innych) uważa się za przejścia stanu maszyny, w których wykonanie bloku może zostać wstrzymane. W każdym z tych punktów maszyna jest przekształcana w wywołanie zwrotne i dołączana do kanału. Kiedy to makro osiąga formę fn, przestaje się ono tłumaczyć. Tak więc możesz wykonywać połączenia tylko pod numerem <! z wewnątrz bloku go, a nie wewnątrz funkcji w bloku kodu.

To część magii core.async. Bez makra go, kod core.async wyglądałby jak callback-hell w innych językach.

+1

Czy mógłbyś wyjaśnić, co masz na myśli przez "zatrzymuje tłumaczenie na granicach funkcji"? – Chiron

+1

dodane do mojego oryginalnego wpisu –

+0

Widzę, więc może core.async może dodać jeszcze jedną metodę do: fn i wyrzucić lepszy wyjątek. To bardzo początkujący nieprzyjazny. – xudifsd

Powiązane problemy