* [PATCH] local-eval, local-compile, and the-environment (v3)
@ 2012-01-15 6:27 Mark H Weaver
2012-01-15 15:39 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-15 6:27 UTC (permalink / raw)
To: guile-devel
[-- Attachment #1: Type: text/plain, Size: 1329 bytes --]
Hello all,
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.
Thanks,
Mark
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: [PATCH] Implement local-eval, local-compile, and the-environment (v3) --]
[-- Type: text/x-patch, Size: 26646 bytes --]
From 215758081534a641df9bc9d8452f4fc35769e8cc Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Tue, 3 Jan 2012 04:02:08 -0500
Subject: [PATCH] Implement `local-eval', `local-compile', and
`the-environment'
* module/ice-9/local-eval.scm: New module (ice-9 local-eval) which
exports `local-eval' and `local-compile'. This module also contains
(non-exported) syntax transformers used internally by psyntax to
implement `the-environment'.
* module/ice-9/psyntax.scm: New core syntax form `the-environment'.
New internal procedure `reachable-bindings' generates the list
of lexical bindings reachable using normal symbols (as opposed to
syntax objects which could reach a larger set of bindings).
* libguile/debug.c (scm_local_eval): New C function that calls the
Scheme implementation of `local-eval' in (ice-9 local-eval).
* libguile/debug.h (scm_local_eval): Add prototype.
* doc/ref/api-evaluation.texi (Local Evaluation): Add documentation.
* test-suite/tests/eval.test (local evaluation): Add tests.
* test-suite/standalone/test-loose-ends.c (test_scm_local_eval):
Add test.
* module/Makefile.am: Add ice-9/local-eval.scm.
* module/ice-9/psyntax-pp.scm: Regenerate from psyntax.scm.
---
doc/ref/api-evaluation.texi | 38 +
libguile/debug.c | 13 +-
libguile/debug.h | 4 +-
module/Makefile.am | 5 +-
module/ice-9/local-eval.scm | 151 +
module/ice-9/psyntax-pp.scm |15387 +++++++++++++++++--------------
module/ice-9/psyntax.scm | 124 +
test-suite/standalone/test-loose-ends.c | 16 +-
test-suite/tests/eval.test | 87 +-
9 files changed, 8846 insertions(+), 6979 deletions(-)
create mode 100644 module/ice-9/local-eval.scm
diff --git a/doc/ref/api-evaluation.texi b/doc/ref/api-evaluation.texi
index 2e48dcb..72dd4df 100644
--- a/doc/ref/api-evaluation.texi
+++ b/doc/ref/api-evaluation.texi
@@ -19,6 +19,7 @@ loading, evaluating, and compiling Scheme code at run time.
* Loading:: Loading Scheme code from file.
* Character Encoding of Source Files:: Loading non-ASCII Scheme code from file.
* Delayed Evaluation:: Postponing evaluation until it is needed.
+* Local Evaluation:: Evaluation in a local lexical environment.
@end menu
@@ -954,6 +955,43 @@ value.
@end deffn
+@node Local Evaluation
+@subsection Local Evaluation
+
+@deffn syntax the-environment
+Captures and returns a lexical environment for use with
+@code{local-eval} or @code{local-compile}.
+@end deffn
+
+@deffn {Scheme Procedure} local-eval exp env
+@deffnx {C Function} scm_local_eval (exp, env)
+Evaluate the expression @var{exp} in the lexical environment @var{env}.
+This mostly behaves as if @var{exp} had been wrapped in a lambda
+expression @code{`(lambda () ,@var{exp})} and put in place of
+@code{(the-environment)}, with the resulting procedure called by
+@code{local-eval}. In other words, @var{exp} is evaluated within the
+lexical environment of @code{(the-environment)}, but within the dynamic
+environment of the call to @code{local-eval}.
+@end deffn
+
+@deffn {Scheme Procedure} local-compile exp env [opts=()]
+Compile the expression @var{exp} in the lexical environment @var{env}.
+If @var{exp} is a procedure, the result will be a compiled procedure;
+otherwise @code{local-compile} is mostly equivalent to
+@code{local-eval}. @var{opts} specifies the compilation options.
+@end deffn
+
+Note that the current implementation of @code{(the-environment)} has
+some limitations. It does not capture local syntax transformers bound
+by @code{let-syntax}, @code{letrec-syntax} or non-top-level
+@code{define-syntax} forms. Any attempt to reference such captured
+syntactic keywords via @code{local-eval} or @code{local-compile}
+produces an error. Also, @code{(the-environment)} does not capture
+lexical bindings that are shadowed by inner bindings with the same name,
+nor hidden lexical bindings produced by macro expansion, even though
+such bindings might be accessible using syntax objects.
+
+
@c Local Variables:
@c TeX-master: "guile.texi"
@c End:
diff --git a/libguile/debug.c b/libguile/debug.c
index 88a01d6..d41acc4 100644
--- a/libguile/debug.c
+++ b/libguile/debug.c
@@ -1,5 +1,5 @@
/* Debugging extensions for Guile
- * Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011 Free Software Foundation
+ * Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -208,6 +208,17 @@ SCM_DEFINE (scm_debug_hang, "debug-hang", 0, 1, 0,
#undef FUNC_NAME
#endif
+SCM
+scm_local_eval (SCM exp, SCM env)
+{
+ static SCM local_eval_var = SCM_BOOL_F;
+
+ if (scm_is_false (local_eval_var))
+ local_eval_var = scm_c_module_lookup
+ (scm_c_resolve_module ("ice-9 local-eval"), "local-eval");
+ return scm_call_2 (SCM_VARIABLE_REF (local_eval_var), exp, env);
+}
+
static void
init_stack_limit (void)
{
diff --git a/libguile/debug.h b/libguile/debug.h
index d862aba..4155d19 100644
--- a/libguile/debug.h
+++ b/libguile/debug.h
@@ -3,7 +3,7 @@
#ifndef SCM_DEBUG_H
#define SCM_DEBUG_H
-/* Copyright (C) 1995,1996,1998,1999,2000,2001,2002,2004,2008,2009,2010
+/* Copyright (C) 1995,1996,1998,1999,2000,2001,2002,2004,2008,2009,2010,2012
* Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
@@ -41,6 +41,8 @@ typedef union scm_t_debug_info
\f
+SCM_API SCM scm_local_eval (SCM exp, SCM env);
+
SCM_API SCM scm_reverse_lookup (SCM env, SCM data);
SCM_API SCM scm_procedure_source (SCM proc);
SCM_API SCM scm_procedure_name (SCM proc);
diff --git a/module/Makefile.am b/module/Makefile.am
index 56fa48d..9c9d8ed 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in.
##
-## Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+## Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
##
## This file is part of GUILE.
##
@@ -243,7 +243,8 @@ ICE_9_SOURCES = \
ice-9/weak-vector.scm \
ice-9/list.scm \
ice-9/serialize.scm \
- ice-9/vlist.scm
+ ice-9/vlist.scm \
+ ice-9/local-eval.scm
SRFI_SOURCES = \
srfi/srfi-1.scm \
diff --git a/module/ice-9/local-eval.scm b/module/ice-9/local-eval.scm
new file mode 100644
index 0000000..7df94e1
--- /dev/null
+++ b/module/ice-9/local-eval.scm
@@ -0,0 +1,151 @@
+;;; -*- mode: scheme; coding: utf-8; -*-
+;;;
+;;; Copyright (C) 2012 Free Software Foundation, Inc.
+;;;
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+(define-module (ice-9 local-eval)
+ #:use-module (ice-9 format)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-9 gnu)
+ #:use-module (system base compile)
+ #:export (local-eval local-compile))
+
+(define-record-type lexical-environment-type
+ (make-lexical-environment module wrapper boxes pattern-bindings
+ var-names pattern-var-names unsupported-names)
+ lexical-environment?
+ (module lexenv-module)
+ (wrapper lexenv-wrapper)
+ (boxes lexenv-boxes)
+ (pattern-bindings lexenv-pattern-bindings)
+ (var-names lexenv-var-names)
+ (pattern-var-names lexenv-pattern-var-names)
+ (unsupported-names lexenv-unsupported-names))
+
+(set-record-type-printer!
+ lexical-environment-type
+ (lambda (e port)
+ (format port "#<lexical-environment ~S ~S ~S ~S>"
+ (module-name (lexenv-module e))
+ (reverse (map (lambda (name box) (list name (box)))
+ (lexenv-var-names e) (lexenv-boxes e)))
+ (reverse (lexenv-pattern-var-names e))
+ (reverse (lexenv-unsupported-names e)))))
+
+(define (local-eval x e)
+ "Evaluate the expression @var{x} within the lexical environment @var{e}."
+ (cond ((lexical-environment? e)
+ (apply (eval ((lexenv-wrapper e) x)
+ (lexenv-module e))
+ (append (lexenv-boxes e)
+ (lexenv-pattern-bindings e))))
+ ((module? e)
+ ;; Here we evaluate the expression within `lambda', and then
+ ;; call the resulting procedure outside of the dynamic extent
+ ;; of `eval'. We do this because `eval' sets (current-module)
+ ;; within its dynamic extent, and we don't want that. Also,
+ ;; doing it this way makes this a proper tail call.
+ ((eval #`(lambda () #,x) e)))
+ (else (error "local-eval: invalid lexical environment" e))))
+
+(define* (local-compile x e #:key (opts '()))
+ "Compile and evaluate the expression @var{x} within the lexical environment @var{e}."
+ (cond ((lexical-environment? e)
+ (apply (compile ((lexenv-wrapper e) x)
+ #:env (lexenv-module e)
+ #:from 'scheme #:opts opts)
+ (append (lexenv-boxes e)
+ (lexenv-pattern-bindings e))))
+ ((module? e)
+ ;; Here we compile the expression within `lambda', and then
+ ;; call the resulting procedure outside of the dynamic extent
+ ;; of `compile'. We do this because `compile' sets
+ ;; (current-module) during evaluation, and we don't want that.
+ ((compile #`(lambda () #,x)
+ #:env e #:from 'scheme #:opts opts)))
+ (else (error "local-compile: invalid lexical environment" e))))
+
+(define-syntax-rule (make-box v)
+ (case-lambda
+ (() v)
+ ((x) (set! v x))))
+
+(define-syntax box-lambda*
+ (lambda (x)
+ (syntax-case x ()
+ ((_ (v ...) (pvar ...) (pvar-lvl ...) (unsupported ...) e)
+ (with-syntax
+ (((nested-pvar ...)
+ (map within-nested-ellipses #'(pvar ...) #'(pvar-lvl ...))))
+ #'(lambda (v ... pvar ...)
+ (let-syntax
+ ((v (identifier-syntax-from-box v))
+ ...
+ (unsupported (unsupported-binding 'unsupported))
+ ...)
+ (with-syntax
+ ((nested-pvar pvar) ...)
+ #f ; force expression context
+ e))))))))
+
+(define-syntax capture-environment
+ (lambda (x)
+ (syntax-case x ()
+ ((_ module (box ...) (v ...) (pvar ...) (pvar-lvl ...) (unsupported ...))
+ (with-syntax
+ (((nested-pvar ...)
+ (map within-nested-ellipses #'(pvar ...) #'(pvar-lvl ...))))
+ #'(make-lexical-environment
+ module
+ (lambda (expression) #`(box-lambda*
+ #,'(v ...)
+ #,'(pvar ...)
+ #,'(pvar-lvl ...)
+ #,'(unsupported ...)
+ #,expression))
+ (list box ...)
+ (list #'nested-pvar ...)
+ '(v ...)
+ '(pvar ...)
+ '(unsupported ...)))))))
+
+(define-syntax-rule (identifier-syntax-from-box box)
+ (make-transformer-from-box
+ (syntax-object-of box)
+ (identifier-syntax (id (box))
+ ((set! id x) (box x)))))
+
+(define-syntax syntax-object-of
+ (lambda (form)
+ (syntax-case form ()
+ ((_ x) #`(quote #,(datum->syntax #'x #'x))))))
+
+(define (make-transformer-from-box id trans)
+ (set-procedure-property! trans 'identifier-syntax-box id)
+ trans)
+
+(define (within-nested-ellipses s lvl)
+ (let loop ((s s) (n (syntax->datum lvl)))
+ (if (zero? n) s (loop #`(#,s (... ...))
+ (- n 1)))))
+
+(define (unsupported-binding name)
+ (make-variable-transformer
+ (lambda (x)
+ (syntax-violation
+ name
+ "unsupported binding captured by (the-environment)"
+ x))))
diff --git a/module/ice-9/psyntax.scm b/module/ice-9/psyntax.scm
index 1bf3c32..66e4583 100644
--- a/module/ice-9/psyntax.scm
+++ b/module/ice-9/psyntax.scm
@@ -786,6 +786,55 @@
id))))))
(else (syntax-violation 'id-var-name "invalid id" id)))))
+ ;;
+ ;; reachable-bindings returns an alist containing one entry
+ ;; (sym . label) for each binding that is accessible using normal
+ ;; symbols.
+ ;;
+ ;; This implementation was derived from that of id-var-name (above),
+ ;; and closely mirrors its structure.
+ ;;
+ (define reachable-bindings
+ (lambda (w)
+ (define scan
+ (lambda (subst marks results)
+ (if (null? subst)
+ results
+ (let ((fst (car subst)))
+ (if (eq? fst 'shift)
+ (scan (cdr subst) (cdr marks) results)
+ (let ((symnames (ribcage-symnames fst)))
+ (if (vector? symnames)
+ (scan-vector-rib subst marks symnames fst results)
+ (scan-list-rib subst marks symnames fst results))))))))
+ (define scan-list-rib
+ (lambda (subst marks symnames ribcage results)
+ (let f ((symnames symnames) (i 0) (results results))
+ (cond
+ ((null? symnames) (scan (cdr subst) marks results))
+ ((and (not (assq (car symnames) results))
+ (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
+ (f (cdr symnames)
+ (fx+ i 1)
+ (cons (cons (car symnames)
+ (list-ref (ribcage-labels ribcage) i))
+ results)))
+ (else (f (cdr symnames) (fx+ i 1) results))))))
+ (define scan-vector-rib
+ (lambda (subst marks symnames ribcage results)
+ (let ((n (vector-length symnames)))
+ (let f ((i 0) (results results))
+ (cond
+ ((fx= i n) (scan (cdr subst) marks results))
+ ((and (not (assq (vector-ref symnames i) results))
+ (same-marks? marks (vector-ref (ribcage-marks ribcage) i)))
+ (f (fx+ i 1)
+ (cons (cons (vector-ref symnames i)
+ (vector-ref (ribcage-labels ribcage) i))
+ results)))
+ (else (f (fx+ i 1) results)))))))
+ (scan (wrap-subst w) (wrap-marks w) '())))
+
;; free-id=? must be passed fully wrapped ids since (free-id=? x y)
;; may be true even if (free-id=? (wrap x w) (wrap y w)) is not.
@@ -1803,6 +1852,81 @@
(_ (syntax-violation 'quote "bad syntax"
(source-wrap e w s mod))))))
+ (global-extend 'core 'the-environment
+ (lambda (e r w s mod)
+ (define ice-9/local-eval
+ (lambda (sym)
+ (wrap sym top-wrap '(private ice-9 local-eval))))
+ (call-with-values
+ (lambda ()
+ (syntax-case e ()
+ ((x) (let ((id (wrap #'x w mod)))
+ (values (syntax-object-wrap id)
+ (syntax-object-module id))))
+ (_ (syntax-violation 'the-environment "bad syntax"
+ (source-wrap e w s mod)))))
+ (lambda (w mod)
+ (with-syntax
+ ((make-box (ice-9/local-eval 'make-box))
+ (module-name (cdr mod)))
+ (let* ((sym+labels (reachable-bindings w))
+ (ids (map (lambda (sym+label)
+ (wrap (car sym+label) w mod))
+ sym+labels))
+ (bindings (map (lambda (sym+label)
+ (lookup (cdr sym+label) r mod))
+ sym+labels))
+ (categories (map (lambda (id b)
+ (case (binding-type b)
+ ((lexical) 'lexical)
+ ((syntax) 'pattern-var)
+ ((macro) (if (procedure-property
+ (binding-value b)
+ 'identifier-syntax-box)
+ 'already-boxed
+ ;; TODO: support macros
+ #f))
+ (else #f)))
+ ids bindings))
+ (maybe-boxes (map (lambda (id b c)
+ (case c
+ ((lexical) #`(make-box #,id))
+ ((already-boxed) (procedure-property
+ (binding-value b)
+ 'identifier-syntax-box))
+ (else #f)))
+ ids bindings categories))
+ (maybe-pattern-bindings (map (lambda (b c)
+ (case c
+ ((pattern-var) (binding-value b))
+ (else #f)))
+ bindings categories)))
+ (with-syntax
+ ((capture-environment (ice-9/local-eval 'capture-environment))
+ (module #'(resolve-module 'module-name))
+ (boxes (filter identity maybe-boxes))
+ (var-ids (filter identity (map (lambda (maybe-box id)
+ (and maybe-box id))
+ maybe-boxes ids)))
+ (pattern-var-ids (filter identity
+ (map (lambda (maybe-pattern-binding id)
+ (and maybe-pattern-binding id))
+ maybe-pattern-bindings ids)))
+ (pattern-var-lvls (filter identity
+ (map (lambda (maybe-pattern-binding)
+ (and maybe-pattern-binding
+ (cdr maybe-pattern-binding)))
+ maybe-pattern-bindings)))
+ (unsupported-ids (filter identity
+ (map (lambda (category id)
+ (and (not category) id))
+ categories ids))))
+ (expand #`(capture-environment
+ module boxes var-ids
+ pattern-var-ids pattern-var-lvls
+ unsupported-ids)
+ r empty-wrap mod))))))))
+
(global-extend 'core 'syntax
(let ()
(define gen-syntax
diff --git a/test-suite/standalone/test-loose-ends.c b/test-suite/standalone/test-loose-ends.c
index 2fdbe7d..f815ae2 100644
--- a/test-suite/standalone/test-loose-ends.c
+++ b/test-suite/standalone/test-loose-ends.c
@@ -3,7 +3,7 @@
* Test items of the Guile C API that aren't covered by any other tests.
*/
-/* Copyright (C) 2009 Free Software Foundation, Inc.
+/* Copyright (C) 2009, 2012 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -43,9 +43,23 @@ test_scm_from_locale_keywordn ()
}
static void
+test_scm_local_eval ()
+{
+ SCM result = scm_local_eval
+ (scm_list_3 (scm_from_latin1_symbol ("+"),
+ scm_from_latin1_symbol ("x"),
+ scm_from_latin1_symbol ("y")),
+ scm_c_eval_string ("(let ((x 1) (y 2)) (the-environment))"));
+
+ assert (scm_is_true (scm_equal_p (result,
+ scm_from_signed_integer (3))));
+}
+
+static void
tests (void *data, int argc, char **argv)
{
test_scm_from_locale_keywordn ();
+ test_scm_local_eval ();
}
int
diff --git a/test-suite/tests/eval.test b/test-suite/tests/eval.test
index a128cd7..6848c5e 100644
--- a/test-suite/tests/eval.test
+++ b/test-suite/tests/eval.test
@@ -1,5 +1,5 @@
;;;; eval.test --- tests guile's evaluator -*- scheme -*-
-;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
@@ -19,7 +19,8 @@
:use-module (test-suite lib)
:use-module ((srfi srfi-1) :select (unfold count))
:use-module ((system vm vm) :select (make-vm call-with-vm))
- :use-module (ice-9 documentation))
+ :use-module (ice-9 documentation)
+ :use-module (ice-9 local-eval))
(define exception:bad-expression
@@ -422,4 +423,86 @@
(thunk (let loop () (cons 's (loop)))))
(call-with-vm vm thunk))))
+;;;
+;;; local-eval
+;;;
+
+(with-test-prefix "local evaluation"
+
+ (pass-if "local-eval"
+
+ (let* ((env1 (let ((x 1) (y 2) (z 3))
+ (define-syntax-rule (foo x) (quote x))
+ (the-environment)))
+ (env2 (local-eval '(let ((x 111) (a 'a))
+ (define-syntax-rule (bar x) (quote x))
+ (the-environment))
+ env1)))
+ (local-eval '(set! x 11) env1)
+ (local-eval '(set! y 22) env1)
+ (local-eval '(set! z 33) env2)
+ (and (equal? (local-eval '(list x y z) env1)
+ '(11 22 33))
+ (equal? (local-eval '(list x y z a) env2)
+ '(111 22 33 a)))))
+
+ (pass-if "local-compile"
+
+ (let* ((env1 (let ((x 1) (y 2) (z 3))
+ (define-syntax-rule (foo x) (quote x))
+ (the-environment)))
+ (env2 (local-compile '(let ((x 111) (a 'a))
+ (define-syntax-rule (bar x) (quote x))
+ (the-environment))
+ env1)))
+ (local-compile '(set! x 11) env1)
+ (local-compile '(set! y 22) env1)
+ (local-compile '(set! z 33) env2)
+ (and (equal? (local-compile '(list x y z) env1)
+ '(11 22 33))
+ (equal? (local-compile '(list x y z a) env2)
+ '(111 22 33 a)))))
+
+ (pass-if "the-environment within a macro"
+
+ (let ()
+ (define-syntax-rule (test)
+ (let ((x 1) (y 2))
+ (the-environment)))
+ (let ((env (let ((x 111) (y 222))
+ (test))))
+ (equal? (local-eval '(list x y) env)
+ '(1 2)))))
+
+ (pass-if "capture pattern variables"
+
+ (let ((env (syntax-case #'(((a 1) (b 2) (c 3))
+ ((d 4) (e 5) (f 6))) ()
+ ((((k v) ...) ...) (the-environment)))))
+ (equal? (syntax->datum (local-eval '#'((k ... v ...) ...) env))
+ '((a b c 1 2 3) (d e f 4 5 6)))))
+
+ (pass-if "mixed primitive-eval, local-eval and local-compile"
+
+ (let* ((env1 (primitive-eval '(let ((x 1) (y 2) (z 3))
+ (define-syntax-rule (foo x) (quote x))
+ (the-environment))))
+ (env2 (local-eval '(let ((x 111) (a 'a))
+ (define-syntax-rule (bar x) (quote x))
+ (the-environment))
+ env1))
+ (env3 (local-compile '(let ((y 222) (b 'b))
+ (the-environment))
+ env2)))
+ (local-eval '(set! x 11) env1)
+ (local-compile '(set! y 22) env2)
+ (local-eval '(set! z 33) env2)
+ (local-compile '(set! a (* y 2)) env3)
+ (and (equal? (local-compile '(list x y z) env1)
+ '(11 22 33))
+ (equal? (local-eval '(list x y z a) env2)
+ '(111 22 33 444))
+ (equal? (local-eval '(list x y z a b) env3)
+ '(111 222 33 444 b))))))
+
;;; eval.test ends here
--
1.7.5.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 6:27 [PATCH] local-eval, local-compile, and the-environment (v3) Mark H Weaver
@ 2012-01-15 15:39 ` David Kastrup
2012-01-15 17:14 ` Mark H Weaver
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-15 15:39 UTC (permalink / raw)
To: Mark H Weaver; +Cc: guile-devel
Mark H Weaver <mhw@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 15:39 ` David Kastrup
@ 2012-01-15 17:14 ` Mark H Weaver
2012-01-15 17:38 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-15 17:14 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 17:14 ` Mark H Weaver
@ 2012-01-15 17:38 ` David Kastrup
2012-01-15 18:15 ` Mark H Weaver
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-15 17:38 UTC (permalink / raw)
To: guile-devel
Mark H Weaver <mhw@netris.org> writes:
> David Kastrup <dak@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 ...).
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.
--
David Kastrup
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 17:38 ` David Kastrup
@ 2012-01-15 18:15 ` Mark H Weaver
2012-01-15 18:26 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-15 18:15 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@gnu.org> writes:
> Mark H Weaver <mhw@netris.org> writes:
>
>> David Kastrup <dak@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.
> 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? What
functionality do you gain?
Mark
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 18:15 ` Mark H Weaver
@ 2012-01-15 18:26 ` David Kastrup
2012-01-15 19:06 ` Mark H Weaver
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-15 18:26 UTC (permalink / raw)
To: guile-devel
Mark H Weaver <mhw@netris.org> writes:
> David Kastrup <dak@gnu.org> writes:
>
>> Mark H Weaver <mhw@netris.org> writes:
>>
>>> David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 18:26 ` David Kastrup
@ 2012-01-15 19:06 ` Mark H Weaver
2012-01-15 19:24 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-15 19:06 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@gnu.org> writes:
> Mark H Weaver <mhw@netris.org> writes:
>
>> David Kastrup <dak@gnu.org> writes:
>>
>>> Mark H Weaver <mhw@netris.org> writes:
>>>
>>>> David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 19:06 ` Mark H Weaver
@ 2012-01-15 19:24 ` David Kastrup
2012-01-15 19:52 ` Mark H Weaver
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-15 19:24 UTC (permalink / raw)
To: guile-devel
Mark H Weaver <mhw@netris.org> writes:
> David Kastrup <dak@gnu.org> writes:
>
>> 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) ?
That is embarrassingly correct. I was thinking "body" here instead of
"form". Obviously, being able to evaluate a single _defining_ form in
its own implicit (let () ...) context is quite useless since there is no
way to subsequently use that definition.
(cons* 'let '() body) is more complex than (cons 'begin body), but not
all that much.
> 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.
--
David Kastrup
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 19:24 ` David Kastrup
@ 2012-01-15 19:52 ` Mark H Weaver
2012-01-15 20:12 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-15 19:52 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 19:52 ` Mark H Weaver
@ 2012-01-15 20:12 ` David Kastrup
2012-01-15 20:36 ` David Kastrup
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-15 20:12 UTC (permalink / raw)
To: guile-devel
Mark H Weaver <mhw@netris.org> writes:
> David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 20:12 ` David Kastrup
@ 2012-01-15 20:36 ` David Kastrup
2012-01-16 5:46 ` Mark H Weaver
2012-01-16 8:44 ` Mark H Weaver
0 siblings, 2 replies; 17+ messages in thread
From: David Kastrup @ 2012-01-15 20:36 UTC (permalink / raw)
To: guile-devel
David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 20:36 ` David Kastrup
@ 2012-01-16 5:46 ` Mark H Weaver
2012-01-16 7:46 ` David Kastrup
2012-01-16 8:44 ` Mark H Weaver
1 sibling, 1 reply; 17+ messages in thread
From: Mark H Weaver @ 2012-01-16 5:46 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@gnu.org> writes:
> 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.
Yes, top-level is a very different animal than an internal body, despite
the superficial syntactic similarities.
> 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).
> 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.
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.
Mark
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-16 5:46 ` Mark H Weaver
@ 2012-01-16 7:46 ` David Kastrup
2012-01-16 7:57 ` Mark H Weaver
0 siblings, 1 reply; 17+ messages in thread
From: David Kastrup @ 2012-01-16 7:46 UTC (permalink / raw)
To: Mark H Weaver; +Cc: guile-devel
Mark H Weaver <mhw@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-16 7:46 ` David Kastrup
@ 2012-01-16 7:57 ` Mark H Weaver
0 siblings, 0 replies; 17+ messages in thread
From: Mark H Weaver @ 2012-01-16 7:57 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-15 20:36 ` David Kastrup
2012-01-16 5:46 ` Mark H Weaver
@ 2012-01-16 8:44 ` Mark H Weaver
2012-01-16 9:07 ` Mark H Weaver
2012-01-16 9:34 ` David Kastrup
1 sibling, 2 replies; 17+ messages in thread
From: Mark H Weaver @ 2012-01-16 8:44 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-16 8:44 ` Mark H Weaver
@ 2012-01-16 9:07 ` Mark H Weaver
2012-01-16 9:34 ` David Kastrup
1 sibling, 0 replies; 17+ messages in thread
From: Mark H Weaver @ 2012-01-16 9:07 UTC (permalink / raw)
To: David Kastrup; +Cc: guile-devel
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] local-eval, local-compile, and the-environment (v3)
2012-01-16 8:44 ` Mark H Weaver
2012-01-16 9:07 ` Mark H Weaver
@ 2012-01-16 9:34 ` David Kastrup
1 sibling, 0 replies; 17+ messages in thread
From: David Kastrup @ 2012-01-16 9:34 UTC (permalink / raw)
To: Mark H Weaver; +Cc: guile-devel
Mark H Weaver <mhw@netris.org> writes:
> David Kastrup <dak@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
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2012-01-16 9:34 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-01-15 6:27 [PATCH] local-eval, local-compile, and the-environment (v3) Mark H Weaver
2012-01-15 15:39 ` David Kastrup
2012-01-15 17:14 ` Mark H Weaver
2012-01-15 17:38 ` David Kastrup
2012-01-15 18:15 ` Mark H Weaver
2012-01-15 18:26 ` David Kastrup
2012-01-15 19:06 ` Mark H Weaver
2012-01-15 19:24 ` David Kastrup
2012-01-15 19:52 ` Mark H Weaver
2012-01-15 20:12 ` David Kastrup
2012-01-15 20:36 ` David Kastrup
2012-01-16 5:46 ` Mark H Weaver
2012-01-16 7:46 ` David Kastrup
2012-01-16 7:57 ` Mark H Weaver
2012-01-16 8:44 ` Mark H Weaver
2012-01-16 9:07 ` Mark H Weaver
2012-01-16 9:34 ` David Kastrup
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).