Core.async, Rules for passing clojure vals to go block

303 views
Skip to first unread message

Timothy Washington

unread,
Dec 25, 2013, 10:35:34 AM12/25/13
to clo...@googlegroups.com
Hi there, 

For core.async, what are the rules for, i) having vals from a function closure, ii) passed into a go block. In the example below, I cannot pass in the system-atom to the handle function. The call will just die silently, without throwing any exception. Instead, I have to pass in a nil for the function to be invoked. 

Is this expected behaviour? Is it a bug? I would've expected an exception to be thrown if that's forbidden. 


  (fn [system-atom handlefn]
    (println "sanity check: " system-atom)
    (go (loop [msg (<! chanl)]
          ;; (handlefn system-atom msg)
(handlefn nil msg)
          (recur (<! chanl)))))


Thanks 

Tim Washington 


Timothy Washington

unread,
Jan 3, 2014, 1:21:16 PM1/3/14
to clo...@googlegroups.com
Forwarding... 

---------- Forwarded message ----------
From: Timothy Washington <twas...@gmail.com>
Date: Fri, Jan 3, 2014 at 1:17 PM
Subject: Re: Core.async, Rules for passing clojure vals to go block
To: Shaun Gilchrist <shaun...@gmail.com>


I'm using Prismatic's Schema in my code base. Now, it looks like defining some functions with s/defn, yields some wonky behaviour. Particularly, not running, when being invoked in a go block. It just fails silently, which is why it was so hard to track down. Don't yet know why this is happening. But an fyi for the devs and users of this package. Still love schema, I just need to figure out where the call chain breaks down. 


Hth 

Tim Washington 


On Fri, Jan 3, 2014 at 9:58 AM, Timothy Washington <twas...@gmail.com> wrote:
Hey Shaun, 

Thanks for looking into this. Your example does indeed work. I'll have to teardown my own code and see where the invocations are failing. At least I know it's not core.async. 


Cheers 

Tim Washington 


On Thu, Jan 2, 2014 at 4:16 PM, Shaun Gilchrist <shaun...@gmail.com> wrote:
Hey, I am attempting to duplicate the issue you described in the email here: http://cljsfiddle.net/fiddle/shaunxcode.timothy-example and it seems to work for me - granted I am doing this in cljs so maybe it is different for clojure? 

Jason Wolfe

unread,
Jan 3, 2014, 2:39:34 PM1/3/14
to clo...@googlegroups.com
Thanks for the report. 

Schema fns inside of go blocks seem to work fine for me.  It seems likely that you're seeing an exception inside the go block, which is swallowed by default:

user> (clojure.core.async/go (println "A"))
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@46ae10a6>
A

user> (clojure.core.async/go (throw (RuntimeException.)) (println "A"))
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@427c78c1>

Would you mind wrapping the body of your go block in a try/catch and printing the exception stack trace, or posting a gist that demonstrates the issue so I can look into it further?

Thanks!

Timothy Washington

unread,
Jan 3, 2014, 6:18:22 PM1/3/14
to clo...@googlegroups.com
Hey Jason, 

You were exactly right (which is pretty impressive, being that you've never seen my code). In my (s/defn ..) form, there was an error that was failing silently. 

(s/defn [one two]
   ...
   #_(def params (atom '({}) ))
   (def params '({}))
   (try (eval `(~afn ~@params))  (catch Exception e (println "Exception: " (.getMessage e)))))

So the abouve code works. But if I instead use the commented version, I'll get an exception. It seems there's a problem passing in a form containing an atom to be dynamically eval'd. The error is mentioned on these posts (here and here). Is there a way to pass in a form containing an atom to be dynamically eval'd? It's pretty important to my architecture, that all functions treat that system-atom the same. Any insights are welcome. 

"java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: clojure.lang.Atom@1c99db7 (NO_SOURCE_FILE:0)"


Thanks 

Tim Washington 

Timothy Baldridge

unread,
Jan 3, 2014, 6:51:33 PM1/3/14
to clo...@googlegroups.com
Using a def inside a defn really shouldn't be done. Perhaps move the params to a let outside of the s/defn? Or change the def to a let?

Timothy Baldridge


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

Timothy Washington

unread,
Jan 3, 2014, 7:04:16 PM1/3/14
to clo...@googlegroups.com
Oh, that was just for demonstration purposes. Those vals are actually getting set in a let (see here). 


Tim Washington 

Jason Wolfe

unread,
Jan 3, 2014, 7:20:55 PM1/3/14
to clo...@googlegroups.com
Glad to help.  

I admittedly haven't taken the time to understand what's going on in your code, but whenever I see `eval` I feel compelled to ask: are you sure you need it?  

With that out of the way, here's a trick I've used to work around related errors:

https://groups.google.com/d/msg/clojure/BZwinR2zNgU/8HGOgzOxzosJ

Best,
Jason

Timothy Washington

unread,
Jan 3, 2014, 7:43:35 PM1/3/14
to clo...@googlegroups.com
The crux of the problem is that the size of params can be variable. Only at runtime, does the code match the loaded afn, with the passed in params. So I basically just need to break out the ~@params into individuated input arguments. The only other way I can think to do that, without using eval, would be to curry afn, for each input param. 


Tim Washington 

Cedric Greevey

unread,
Jan 3, 2014, 7:45:04 PM1/3/14
to clo...@googlegroups.com
Your best bet is to move your "system atom" out to a top-level def, like so:

(def system-atom (atom '({})))

Then just use (eval `(~afn system-atom)) later on, from places where the system-atom Var is visible (same ns, or :use'd), or use (eval `(~afn x/system-atom)) to qualify the name. The syntax quote will cause the eval'd code to use a fully qualified name regardless.

If you don't want lots of stuff depending on a top-level singleton object (say, because you might want to have two parallel tasks in the same JVM with two different system-atoms), and can't get rid of eval, you can use a top-level map:

(def system-atoms (atom {}))

(defn new-system-atom! [k]
  (swap! system-atoms assoc k (atom '({}))))

...

(new-system-atom! :foo) ... (eval `(~afn (:foo @system-atoms)))

...

(eval `(~bfn (:bar @system-atoms)))



Cedric Greevey

unread,
Jan 3, 2014, 7:49:37 PM1/3/14
to clo...@googlegroups.com
Amend what I said: your params would need to be something like `(... system-atom ...), or `(... a-namespace/system-atom ...), or `(... (:foo @system-atoms) ...). Then (eval `(~afn ~@params)) will splice that in and the resulting code will look up the appropriate Var (and then the appropriate map key, in the latter case) to get at the atom.


Jason Wolfe

unread,
Jan 3, 2014, 8:11:54 PM1/3/14
to clo...@googlegroups.com
On Fri, Jan 3, 2014 at 4:43 PM, Timothy Washington <twas...@gmail.com> wrote:
The crux of the problem is that the size of params can be variable. Only at runtime, does the code match the loaded afn, with the passed in params. So I basically just need to break out the ~@params into individuated input arguments. The only other way I can think to do that, without using eval, would be to curry afn, for each input param. 

Can you just use `apply`?  
 
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/j8QWNnFNIVg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Cedric Greevey

unread,
Jan 3, 2014, 8:19:03 PM1/3/14
to clo...@googlegroups.com
Yeah, he probably could. But maybe he has a good reason for avoiding it that we're not aware of. Though I'm not sure what that could be.


You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Timothy Washington

unread,
Jan 3, 2014, 8:48:50 PM1/3/14
to clo...@googlegroups.com
Yes, good point. I tried apply, but the result from that is always nil (where there should be a value). You're right that it should work (we wouldn't use eval in that scenario), but it doesn't. 

I tried putting params in a seq a list and a vector, but no dice. I'm still playing around with it.


Tim Washington 

Timothy Washington

unread,
Jan 3, 2014, 9:39:26 PM1/3/14
to clo...@googlegroups.com
Ok, got it. It was an oversight on my part. From a config file, I was pulling in myns/myfn. But this is just a symbol. I needed to resolve it, and deref the var that it pointed to. So now the below works. Tricky, but I should have caught it earlier. 

(apply @(resolve afn) params)



Thanks guys :) 

Tim Washington 
Reply all
Reply to author
Forward
0 new messages