Fork me on GitHub
#core-async
<
2017-08-08
>
ajmagnifico16:08:11

Question: Can someone help me understand the error message I’m getting here?

(ns sandbox.async
  (:use clojure.core.async))

(<!!
 (let [out-mix (mix (chan))
       [ch-a ch-b ch-c] [(chan) (chan) (chan)]
       n 10]
   (admix out-mix ch-a)
   (admix out-mix ch-b)
   (admix out-mix ch-c)
   (go
     (dotimes [i n] (>! ch-a (str "A" i)))
     (close! ch-a))
   (go
     (dotimes [i n] (>! ch-b (str "B" i)))
     (close! ch-b))
   (go
     (dotimes [i n] (>! ch-c (str "C" i)))
     (close! ch-c))
   (go-loop [x (<! out-mix)]
     (println x)
     (when x (recur (<! out-mix))))))
java.lang.IllegalArgumentException: No implementation of method: :take! of protocol: #'clojure.core.async.impl.protocols/ReadPort found for class: clojure.core.async$mix$reify__25479
	at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:583)
	at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:575)
	at clojure.core.async.impl.protocols$eval17719$fn__17720$G__17710__17727.invoke(protocols.clj:15)
	at clojure.core.async.impl.ioc_macros$take_BANG_.invokeStatic(ioc_macros.clj:983)
	at clojure.core.async.impl.ioc_macros$take_BANG_.invoke(ioc_macros.clj:982)
	at sandbox.async$eval28394$fn__28656$state_machine__23947__auto____28669$fn__28671.invoke(form-init833577736485949258.clj:14)
	at sandbox.async$eval28394$fn__28656$state_machine__23947__auto____28669.invoke(form-init833577736485949258.clj:14)
	at clojure.core.async.impl.ioc_macros$run_state_machine.invokeStatic(ioc_macros.clj:973)
	at clojure.core.async.impl.ioc_macros$run_state_machine.invoke(ioc_macros.clj:972)
	at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invokeStatic(ioc_macros.clj:977)
	at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invoke(ioc_macros.clj:975)
	at sandbox.async$eval28394$fn__28656.invoke(form-init833577736485949258.clj:14)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)

noisesmith16:08:51

that first part of the output indicates to me that you are trying to treat an inner class created by mix as if it were a readable channel

noisesmith17:08:42

mix expects to take an out chan as an arg, I think you need to read from that out chan (which you currently don’t bind)

joshjones17:08:42

you don’t take from a mix, you take from a chan that you give to a mix

noisesmith17:08:54

haha! barely beat you to it, just by reading a stack trace

joshjones17:08:37

well, i knew from the stack trace that this was the case, but i don’t use mixes (hardly ever) so had to see what does what 🙂

noisesmith17:08:59

I’m just joking anyway - I don’t use mixes either

joshjones17:08:04

yeah i know bro

joshjones17:08:38

i’ve mistakenly called protocol functions enough times on something that doesn’t implement the protocol to know that error picard-facepalm

ajmagnifico17:08:17

awesome, thanks guys!

ajmagnifico17:08:31

Okay, that totally fixed it.

ajmagnifico17:08:06

That also answers my questions about close semantics when using mixes

noisesmith17:08:39

@ajmagnifico also, as a general rule, it’s a good pattern to write code that uses channels so that it takes an in-chan and / or out chan as args

noisesmith17:08:56

that way the calling code can decide how it needs to buffer / xform with transducers / etc.

noisesmith17:08:11

and you can also simplify your code by not creating your own chans all the time

noisesmith17:08:44

(which is why mix is accepting a chan arg here for example)

ajmagnifico17:08:29

thanks @noisesmith , I understand the words you’re using, but am having a hard time grokking it. Do you know of any examples of this that you could point me to?

ajmagnifico17:08:05

In general, you’re just saying ---- if I write functions that make use of channels, then give an in-channel and/or out-channel as a parameter?

noisesmith17:08:09

@ajmagnifico it’s a design principle - for example what if you wanted to print everything that goes out of the mix

ajmagnifico17:08:39

(sorry, new baby at home = not a lot of sleep lately 😄 )

noisesmith17:08:25

if so, you can do (let [c (chan 10 (map #(doto % println))) m (mix c)] …)

noisesmith17:08:49

that’s a transducer that adds printing to each item as it comes through the mix, without having two write a bunch of glue code to process the items

noisesmith17:08:19

and the general idea is if you write code that expects to take a chan as an arg, client code can leverage that

noisesmith17:08:25

it makes code more extensible

ajmagnifico17:08:00

okay, I get what you’re saying

ajmagnifico17:08:20

I’m going to keep that in mind as I move forward, and you’ll probably see me in here again asking for feedback at some point