On Mon, Apr 07, 2014 at 11:23:31PM -0700, Peter West wrote: > On Tuesday, 8 April 2014 12:20:16 UTC+10, Carlo wrote: > > 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 > > If that were the case, then the reference to "r" in alternative 2 "(def > lineseq (line-seq r))" would not work; but it does, so "r" is in scope. > Therefore, linseed from alternative 1 must also be in scope, mustn't it? > It was my understanding that the scope of with-open was equivalent to that > of let in that bindings were visible within the entire scope of the > with-open.
No, you misunderstand me. The issue is that "eval" compiles and runs the code at run-time, whereas the "def" compiles at compile time and runs at run-time. In the case of the "def" form, it's compiled and run in the environment of the with-open, so the "r" resolves correctly to the one in the with-open form. The eval, however, runs in a separate environment. You've quoted your "lineseq" (so it's not looked up in the current environment), then eval can't find it in the (new) eval environment. It would have to be looking in the existing environment. (These terms are poorly used, but essentially: the eval loses the lexical context of where it is placed and "lineseq" can't be located.) This is why Marc recommended unquoting lineseq (using ~). An unquoted form will be evaluated, which for a symbol means looking it up in its current lexical scope. > > 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.) > > > > I think this will run into the same problem, for reasons mentioned above. Have you tried actually running it? I'm confident that this will not have the issues that you had with your eval approach. The difference is that it is a macro, so it is returning code to be run, rather than attempting to compile and run code at run-time (as eval does). > > 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. This form of "lines-only" is a function, but it uses the function "intern" instead of the special form "def". This lets us maintain our lexical context while also re-binding a name in the top-level namespace.
signature.asc
Description: Digital signature