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
