On Mon, Apr 07, 2014 at 04:08:03AM -0700, Peter West wrote: > I'm trying to understand the difference between two alternatives in the > following code that reads from a resource file. > > (defn vcf-res-reader > [res] > (->> res > io/resource > io/reader)) > > (defn lines-only > [varname prom resource] > (with-open [r (vcf-res-reader resource) > ; alternative 1 > ;lineseq (line-seq r) > ] > ; alternative 2 > (def lineseq (line-seq r)) > (eval `(def ~(symbol varname) lineseq)) > @prom)) > > (defn lazy-lines > [varname prom resource] > (future > (lines-only varname prom resource) > )) > > > As the code stands, with alternative 2 enabled, the eval setting a named > var to the value of "linseq" works as intended. However, if I disable > alternative 2 and enable alternative 1, and I attempt to access the named > var ("cards" in this case), I get > #<Unbound Unbound: #'vcf.core/cards>
You have a few problems with your code as it stands. I'll look at each of the two alternatives separately. First, alternative 1: > (defn lines-only > [varname prom resource] > (with-open [r (vcf-res-reader resource) > lineseq (line-seq r)] > (eval `(def ~(symbol varname) lineseq)) > @prom)) Your issue here is that the symbol "lineseq" in the eval form doesn't have a name to refer to. You do have a local binding for lineseq, but it's not visible to the eval: (let [x 10] (eval 'x)) ;=> Unable to resolve symbol: x The lineseq binding should really be made in a "let", too, rather than a with-open. The result of a line-seq can't be closed, so it's not really sensible to put it in a "with-open". Now, on to alternative 2: > (defn lines-only > [varname prom resource] > (with-open [r (vcf-res-reader resource)} > (def lineseq (line-seq r)) > (eval `(def ~(symbol varname) lineseq)) > @prom)) This time, you do have something for "lineseq" to refer to! The "def" special form has created a global binding for "lineseq". Now, at the top level of your application you can lookup "lineseq" and get a value, but this isn't good, because your function now modifies the global scope, just to hold a temporary value. There are two ways for you to resolve this. What you're writing sounds a little bit like it should be a macro, so you could write it as such: (defmacro lines-only [varname prom resource] `(with-open [r# (vcf-res-reader ~resource)] (let [lineseq# (line-seq r#)] (def ~(symbol varname) lineseq#) @~prom))) (I think that's right, but I've not actually tested it.) Alternatively, you could write it as a function and use "intern": (defn lines-only [varname prom resource] (with-open [r (vcf-res-reader resource)] (let [lineseq (line-seq r)] (intern *ns* (symbol varname) lineseq) @prom))) I'm less confident about this approach, but it should work. All this being said: it's generally a bad idea to have a macro/function modify the global scope without it being very clear that it's doing so. I usually try to have my macro names start with "def" if they're modifying the global scope (because they're "defining" something is my logic).
signature.asc
Description: Digital signature