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.

Attachment: signature.asc
Description: Digital signature

Reply via email to