Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-16 Thread Mark H Weaver
David Kastrup d...@gnu.org writes:

 (define current-module
   (let ((top-level (the-environment)))
 (lambda () (eval '(the-environment) top-level

Some more notes about the above code (changing `eval' == `local-eval'):

* (local-eval '(the-environment) environment) is a no-op: it always
  returns the same environment that was passed in, so there's no point
  in doing it.

* Of course `top-level' is a constant, and would be even if it were
  within the (lambda () ...) because it captures no lexicals.  It is
  always in the same module, i.e. the module containing the above
  definition.  This same constant value is always returned by
  (current-module).

* Also note that the real `current-module' simply accesses a fluid,
  which can also be set by `set-current-module'.  (Fluids are a scheme
  analogue to dynamically-scoped variables in Lisp).  Conceptually, it
  is variable that is explicitly set by the user.  It has no relation to
  the code that is currently executing.  Rather, it is used during
  compilation (or within a REPL) to keep track of where the user would
  like top-level definitions to go.

 Mark



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-16 Thread Mark H Weaver
I wrote:
 * Also note that the real `current-module' simply accesses a fluid,
   which can also be set by `set-current-module'.  (Fluids are a scheme
   analogue to dynamically-scoped variables in Lisp).  Conceptually, it
   is variable that is explicitly set by the user.  It has no relation to
   the code that is currently executing.  Rather, it is used during
   compilation (or within a REPL) to keep track of where the user would
   like top-level definitions to go.

That last bit was not quite right, let me try again:

   The `current-module' is used during compilation, within a REPL, or by
   primitive-eval, to keep track of which module the user would like
   top-level forms to be compiled in.  It is set at compile time by
   constructs such as `define-module' or (eval-when (compile)
   (set-current-module module)), and this module is baked into every
   identifier at compile time before macro processing takes place.

   Macros in general might slice and dice and mix together fragments of
   code from many different modules.  When this jumble of macro-expanded
   code is evaluated, (current-module) is no longer relevant to it.
   Instead, each top-level variable reference uses the module that was
   baked into its identifier before macro expansion.

  Mark



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-16 Thread David Kastrup
Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 (define current-module
   (let ((top-level (the-environment)))
 (lambda () (eval '(the-environment) top-level

 Some more notes about the above code (changing `eval' == `local-eval'):

 * (local-eval '(the-environment) environment) is a no-op: it always
   returns the same environment that was passed in, so there's no point
   in doing it.

I think this was based on the the current module is part of the
environment, but local-eval does not change (current-module) mantra
which I interpreted in a confused manner.  The interesting thing is that
it is the _lexically_ current module that is part of the environment,
and inside of local-eval, this may well differ from the _actually_
current module as given by (current-module).

-- 
David Kastrup



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread David Kastrup
Mark H Weaver m...@netris.org writes:

 Here's the third version of my simple `local-eval' patch.
 Notable changes from last time:

 * Pattern variables are now captured properly.
 * `the-environment' now works as advertised within macro definitions.
 * Added doc strings for `local-eval' and `local-compile'.

 I am open to reimplementing this in a different way for 2.0.5, along the
 lines suggested by Andy.  I'd like to capture all bindings, not just the
 ones reachable by symbols.  I'd like to support _all_ lexical bindings,
 including local syntax transformers.  I'm also warming to the idea of
 standardizing on variable objects as a way to represent mutable
 lexicals.  However, there's no time to do this for 2.0.4.  That job
 depends on other big jobs, notably a major overhaul of the evaluator.

 Nonetheless, I think it is very important to include this simple
 implementation in 2.0.4.  This is a BUG FIX, the bug being that
 `local-eval' was removed from underneath Lilypond's feet.  A partial
 implementation (that almost certainly does everything they need) is
 _far_ better than none at all.  Lilypond can only depend on `local-eval'
 if installations of Guile without it are quite rare.  If we can get this
 in 2.0.4, there's hope that we can make Guile 2.0.[0-3] rare.

 Please consider it.  This implementation is quite robust.

I am not altogether comfortable with pushing a temporary fix for the
sake of LilyPond when we'll get another behavioral change with the next
version.  LilyPond would be mostly left in the rain for older versions
unless we made that implementation a fallback within our own sources.
However, there would be no point in adopting a fallback implementation
that would not also work with 2.0.0-2.0.3.

As far as I understand, this implementation could be pulled into a
separate file and used for bridging the 2.0.0-2.0.3 gap.  If it is solid
enough to be maintained as 2.0.4, it would likely be a reasonably good
bet.

Of course, a version that would be tighter integrated with the compiler
and optimizer would seem like a more powerful prospect, but it also
sounds that it would need more thorough maintenance to not fall into bit
rot.

I am not sure that the reasons for not permitting definition context in
local-eval are not of somewhat more theoretical than practical nature,
and in particular it sounds to me like I'm also warming to the idea of
standardizing on variable objects as a way to represent mutable
lexicals could suggest that the strength of even the theoretical
reasons might get eroded further.  Of course, _calling_ mutual recursive
functions in execution before the second part has been defined will
remain unfeasible.

-- 
David Kastrup



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread Mark H Weaver
David Kastrup d...@gnu.org writes:
 I am not altogether comfortable with pushing a temporary fix for the
 sake of LilyPond when we'll get another behavioral change with the next
 version.

But there would _not_ be a behavioral change in the next version.
It would only be a change in the internal implementation.

 As far as I understand, this implementation could be pulled into a
 separate file and used for bridging the 2.0.0-2.0.3 gap.

Unfortunately, any implementation of `local-eval' requires changes to
psyntax.scm, which is not something that you could reasonably do as an
external package.

 I am not sure that the reasons for not permitting definition context in
 local-eval are not of somewhat more theoretical than practical nature,

There's at least one practical reason not to allow it, namely that it is
_impossible_ to implement.  Consider this:

  (let ((x 1))
(define (get-x) x)
(the-environment))

If we allow (the-environment) to add definitions to the implicit
`letrec', then (get-x) cannot know which binding of `x' to use.  In
fact, it cannot lookup _any_ bindings, because absolutely any identifier
(even syntactic keywords) could be redefined within this implicit
`letrec'.

This means that it's impossible to compile or evaluate anything within
the body of the outer `let', which means it's not even possible to
evaluate (the-environment) itself.  So there's no way to run the code
above if we allow (the-environment) to add definitions.

 Mark



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread David Kastrup
Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 I am not sure that the reasons for not permitting definition context in
 local-eval are not of somewhat more theoretical than practical nature,

 There's at least one practical reason not to allow it, namely that it is
 _impossible_ to implement.  Consider this:

   (let ((x 1))
 (define (get-x) x)
 (the-environment))

 If we allow (the-environment) to add definitions to the implicit
 `letrec',

 Then just let's not allow it.  Consider the body of local-eval to be
 wrapped inside of an implicit (begin ...).

 I think you mean an implicit (let () ...).  If that's what you want,
 then you can do it yourself, and the result will be less likely to
 confuse.

What is confusing here?  Obviously, definitions made in the scope of
local-eval can't retroactively affect the environment where
the-environment was called.  And now instead of continuing with the
unfriendly For this reason, they are prohibited. we continue with You
may consider the local-eval body as being wrapped inside of an implicit
(let () ...).

I repeat: is there any valid construct under the currently proposed
local-eval code that would change its behavior?

 Is there any currently valid construct that would change its
 behavior?  If not, you _gain_ functionality, and the resulting
 semantics are still straightforward to explain as far as I can see.

 I don't understand this at all.  Change what behavior?

The behavior of local-eval given arbitrary expressions.  Are there any
expressions currently valid that would change their behavior if the body
of local-eval were wrapped in an implicit (let () ...)?

 What functionality do you gain?

The ability to make definitions valid in the local-eval without having
to write (let () ...).  Again: any drawback that is more substantial
than I just don't want you to do that?

-- 
David Kastrup




Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread Mark H Weaver
David Kastrup d...@gnu.org writes:

 Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 I am not sure that the reasons for not permitting definition context in
 local-eval are not of somewhat more theoretical than practical nature,

 There's at least one practical reason not to allow it, namely that it is
 _impossible_ to implement.  Consider this:

   (let ((x 1))
 (define (get-x) x)
 (the-environment))

 If we allow (the-environment) to add definitions to the implicit
 `letrec',

 Then just let's not allow it.  Consider the body of local-eval to be
 wrapped inside of an implicit (begin ...).

 I think you mean an implicit (let () ...).  If that's what you want,
 then you can do it yourself, and the result will be less likely to
 confuse.

 What is confusing here?  Obviously, definitions made in the scope of
 local-eval can't retroactively affect the environment where
 the-environment was called.  And now instead of continuing with the
 unfriendly For this reason, they are prohibited. we continue with You
 may consider the local-eval body as being wrapped inside of an implicit
 (let () ...).

If you want to include local definitions, then you'll need to wrap the
form passed to `local-eval' within (begin ...) anyway.  If you're doing
that, why not just wrap them in (let () ...) instead?  Is it really more
work to type #`(let () #,@forms) than #`(begin #,@forms) ?

I would like for users to be able to use the simple mental model that
the local expression is simply put in place of (the-environment).
I'd also like to achieve the following source-level equivalence:

  expr == (local-eval 'expr (the-environment))

You are suggesting that we wrap the expression within a (let () ...),
for the dubious benefit of allowing you to wrap the local forms in
(begin ...) instead of (let () ...).

I don't see any compelling benefit to this.  On the other hand, I see
less elegant semantics and potential confusion among users, who might
reasonably expect the definitions in their (begin ...) to be added to
the implicit `letrec', as would happen if the (begin ...) were put in
place of (the-environment).

 Mark



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread Mark H Weaver
David Kastrup d...@gnu.org writes:

 You are suggesting that we wrap the expression within a (let () ...),
 for the dubious benefit of allowing you to wrap the local forms in
 (begin ...) instead of (let () ...).

 Are there even situations where you could put definitions after begin?
 How are they different from the situation where you can't?

 I don't see any compelling benefit to this.  On the other hand, I see
 less elegant semantics and potential confusion among users, who might
 reasonably expect the definitions in their (begin ...) to be added to
 the implicit `letrec', as would happen if the (begin ...) were put in
 place of (the-environment).

 begin can start with definitions, but not always?  But (let () ...) can?

 Scheme is weird.

`begin' is indeed quite weird in Scheme.  Its meaning depends on the
context where it is found:

* In expression context, it is like `progn' in Lisp.  It evaluates the
  expressions in order from left to right, and returns the value(s) of
  the last expression.

* At the top-level, or within an internal body where local definitions
  are allowed (i.e. if every form above it in the internal body is a
  definition), it is a _splicing_ operator: it acts as if the contents
  of the begin were spliced into the surrounding context.  This is
  mainly for the benefit of macros, so that they may expand into
  multiple definitions.

For example:

  (let ((x 1))
(define (get-x) x)
(begin
  (define x 2)
  (get-x)))

is equivalent to:

  (let ((x 1))
(define (get-x) x)
(define x 2)
(get-x))

which is equivalent to:

  (let ((x 1))
(letrec ((get-x (lambda () x))
 (x 2))
  (get-x)))

and therefore the result is 2, because the reference to `x' within
(get-x) refers to the inner binding of `x'.

 Mark



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread David Kastrup
Mark H Weaver m...@netris.org writes:

 David Kastrup d...@gnu.org writes:

 You are suggesting that we wrap the expression within a (let () ...),
 for the dubious benefit of allowing you to wrap the local forms in
 (begin ...) instead of (let () ...).

 Are there even situations where you could put definitions after begin?
 How are they different from the situation where you can't?

 I don't see any compelling benefit to this.  On the other hand, I see
 less elegant semantics and potential confusion among users, who might
 reasonably expect the definitions in their (begin ...) to be added to
 the implicit `letrec', as would happen if the (begin ...) were put in
 place of (the-environment).

 begin can start with definitions, but not always?  But (let () ...) can?

 Scheme is weird.

 `begin' is indeed quite weird in Scheme.  Its meaning depends on the
 context where it is found:

 * In expression context, it is like `progn' in Lisp.  It evaluates the
   expressions in order from left to right, and returns the value(s) of
   the last expression.

 * At the top-level, or within an internal body where local definitions
   are allowed (i.e. if every form above it in the internal body is a
   definition), it is a _splicing_ operator: it acts as if the contents
   of the begin were spliced into the surrounding context.  This is
   mainly for the benefit of macros, so that they may expand into
   multiple definitions.

Well, what if begin were able to splice definitions into the original
lexical context of the-environment?  I think it would be strange if a
continuation taken in that context would get to see new definitions.
And if a continuation there does not get to see it, it would be strange
if successive calls of local-eval on the same environment got to see it.

So there is not much of a sensible option except an implicit (let ()
...) wrapper for the sake of splicing begin, or bugging out for new
definitions.

In the light of local-eval evaluating a _form_ rather than a _body_
(stupid of me to forget), and seeing the weird semantics of begin, I
agree that an implicit (let () ...) wrapper for a single form does not
really appear to add significant gains.  It would be marginally
convenient and marginally confusing.

So one might as well forget about it.

-- 
David Kastrup




Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread David Kastrup
David Kastrup d...@gnu.org writes:

 In the light of local-eval evaluating a _form_ rather than a _body_
 (stupid of me to forget), and seeing the weird semantics of begin, I
 agree that an implicit (let () ...) wrapper for a single form does not
 really appear to add significant gains.  It would be marginally
 convenient and marginally confusing.

 So one might as well forget about it.

Except possibly for symmetry: how does plain eval handle it?

guile (let ((x 2)) (eval '(begin (define x 4) x) (current-module)))
4
guile x
4
guile

Right through to the top.  And we couldn't do that in local-eval.  But
it also has no qualms just because previously evaluated forms would have
used a previous definition of x.  But that's because of top-level.  Eval
is always top-level, and define acts like set! there.

What if the-environment had been taken at top-level (basically just
carrying the information of (current-module))?  Should local-eval then
behave accordingly?  If so, could we not just fold eval and local-eval
into one function?  And one could then define
(define current-module
  (let ((top-level (the-environment)))
(lambda () (eval '(the-environment) top-level
if the-environment just returns the current module when at top level?

Basically, I can accept your reasoning.  This is just putting out a bit
of brainstorming.

-- 
David Kastrup




Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread David Kastrup
Mark H Weaver m...@netris.org writes:

 What if the-environment had been taken at top-level (basically just
 carrying the information of (current-module))?  Should local-eval then
 behave accordingly?  If so, could we not just fold eval and local-eval
 into one function?

 This is a good question.  Unfortunately, there is a non-trivial
 difference in the semantics of `eval' vs `local-eval'.

 `eval' temporarily sets the (current-module) to its second argument
 during evaluation of the expression.

 `local-eval' does not do this, and as I recall that was something you
 felt strongly about (and I agree).

I don't think I would feel as strong about it if the argument was a
module, implicating top-level.  It is probably not all that easy to get
the-environment at top level.

 And one could then define
 (define current-module
   (let ((top-level (the-environment)))
 (lambda () (eval '(the-environment) top-level
 if the-environment just returns the current module when at top level?

 I see what you're getting at, but this would be a bad idea, because it
 still makes sense to have `module' be an independent type.

It might come handy to have local-eval not balk upon getting a module,
but without letting it set the current module.  At times, this
functionality might come in handy.  It would also be clear then why eval
would have to balk on every non-module (== non-top-level) environment.

 Also, in the code above, `top-level' would not actually be a top-level
 environment, because (the-environment) is not a top-level form.  If you
 put (define x 5) in place of (the-environment), it would not set a
 top-level variable; it would produce an error.

I am not saying that the actual code would need to work.  It was more
intended as a show of equivalences.

-- 
David Kastrup



Re: [PATCH] local-eval, local-compile, and the-environment (v3)

2012-01-15 Thread Mark H Weaver
David Kastrup d...@gnu.org writes:
 It might come handy to have local-eval not balk upon getting a module,
 but without letting it set the current module.

Agreed, and my implementation of `local-eval' does exactly that.

 Mark