mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-12 23:00:22 +02:00
Add notion of declarative modules
* doc/ref/api-modules.texi (Declarative Modules): New subsection. * module/ice-9/boot-9.scm (module): Change eval-closure slot, which was deprecated and unused, to be a "declarative?" slot, indicating that definitions from the module are declarative. (user-modules-declarative?): New parameter. (make-fresh-user-module): Set declarative according to parameter. (define-module*, define-module): Add #:declarative? keyword argument, defaulting to the value of user-modules-declarative? parameter when the module was expanded. (guile-user): This module is not declarative. * module/language/tree-il/letrectify.scm (compute-declarative-toplevels): Use the new declarative? module flag.
This commit is contained in:
parent
d7bbf6d5db
commit
356ea09719
3 changed files with 106 additions and 31 deletions
|
@ -1,6 +1,6 @@
|
||||||
@c -*-texinfo-*-
|
@c -*-texinfo-*-
|
||||||
@c This is part of the GNU Guile Reference Manual.
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
@c Copyright (C) 1996, 1997, 2000-2004, 2007-2014
|
@c Copyright (C) 1996, 1997, 2000-2004, 2007-2014, 2019
|
||||||
@c Free Software Foundation, Inc.
|
@c Free Software Foundation, Inc.
|
||||||
@c See the file guile.texi for copying conditions.
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ be used for interacting with the module system.
|
||||||
* R6RS Libraries:: The library and import forms.
|
* R6RS Libraries:: The library and import forms.
|
||||||
* Variables:: First-class variables.
|
* Variables:: First-class variables.
|
||||||
* Module System Reflection:: First-class modules.
|
* Module System Reflection:: First-class modules.
|
||||||
|
* Declarative Modules:: Allowing Guile to reason about modules.
|
||||||
* Accessing Modules from C:: How to work with modules with C code.
|
* Accessing Modules from C:: How to work with modules with C code.
|
||||||
* provide and require:: The SLIB feature mechanism.
|
* provide and require:: The SLIB feature mechanism.
|
||||||
* Environments:: R5RS top-level environments.
|
* Environments:: R5RS top-level environments.
|
||||||
|
@ -911,6 +912,69 @@ environment. If you find yourself using one of them, please contact the
|
||||||
Guile developers so that we can commit to stability for that interface.
|
Guile developers so that we can commit to stability for that interface.
|
||||||
|
|
||||||
|
|
||||||
|
@node Declarative Modules
|
||||||
|
@subsection Declarative Modules
|
||||||
|
|
||||||
|
The first-class access to modules and module variables described in the
|
||||||
|
previous subsection is very powerful and allows Guile users to build
|
||||||
|
many tools to dynamically learn things about their Guile systems.
|
||||||
|
However, as Scheme godparent Mathias Felleisen wrote in ``On the
|
||||||
|
Expressive Power of Programming Languages'', a more expressive language
|
||||||
|
is necessarily harder to reason about. There are transformations that
|
||||||
|
Guile's compiler would like to make which can't be done if every
|
||||||
|
top-level binding is subject to mutation at any time.
|
||||||
|
|
||||||
|
Consider this module:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-module (boxes)
|
||||||
|
#:export (make-box box-ref box-set! box-swap!))
|
||||||
|
|
||||||
|
(define (make-box x) (list x))
|
||||||
|
(define (box-ref box) (car box))
|
||||||
|
(define (box-set! box x) (set-car! box x))
|
||||||
|
(define (box-swap! box x)
|
||||||
|
(let ((y (box-ref box)))
|
||||||
|
(box-set! box x)
|
||||||
|
y))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Ideally you'd like for the @code{box-ref} in @code{box-swap!} to be
|
||||||
|
inlined to @code{car}. Guile's compiler can do this, but only if it
|
||||||
|
knows that @code{box-ref}'s definition is what it appears to be in the
|
||||||
|
text. However, in the general case it could be that a programmer could
|
||||||
|
reach into the @code{(boxes)} module at any time and change the value of
|
||||||
|
@code{box-ref}.
|
||||||
|
|
||||||
|
To allow Guile to reason about the values of top-levels from a module, a
|
||||||
|
module can be marked as @dfn{declarative}. This flag applies only to
|
||||||
|
the subset of top-level definitions that are themselves declarative:
|
||||||
|
those that are defined within the compilation unit, and not assigned
|
||||||
|
(@code{set!}) or redefined within the compilation unit.
|
||||||
|
|
||||||
|
To explicitly mark a module as being declarative, pass the
|
||||||
|
@code{#:declarative?} keyword argument when declaring a module:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-module (boxes)
|
||||||
|
#:export (make-box box-ref box-set! box-swap!)
|
||||||
|
#:declarative? #t)
|
||||||
|
@end example
|
||||||
|
|
||||||
|
By default, modules are compiled declaratively if the
|
||||||
|
@code{user-modules-declarative?} parameter is true when the
|
||||||
|
module is compiled.
|
||||||
|
|
||||||
|
@deffn {Scheme Parameter} user-modules-declarative-by-default?
|
||||||
|
A boolean indicating whether definitions in modules created by
|
||||||
|
@code{define-module} or implicitly as part of a compilation unit without
|
||||||
|
an explicit module can be treated as declarative.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Because it's usually what you want, the default value of
|
||||||
|
@code{user-modules-declarative?} is @code{#t}.
|
||||||
|
|
||||||
|
|
||||||
@node Accessing Modules from C
|
@node Accessing Modules from C
|
||||||
@subsection Accessing Modules from C
|
@subsection Accessing Modules from C
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
;;; -*- mode: scheme; coding: utf-8; -*-
|
;;; -*- mode: scheme; coding: utf-8; -*-
|
||||||
|
|
||||||
;;;; Copyright (C) 1995-2014, 2016-2018 Free Software Foundation, Inc.
|
;;;; Copyright (C) 1995-2014, 2016-2019 Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -1744,7 +1744,10 @@ name extensions listed in %load-extensions."
|
||||||
;;; Every module object is of the type 'module-type', which is a record
|
;;; Every module object is of the type 'module-type', which is a record
|
||||||
;;; consisting of the following members:
|
;;; consisting of the following members:
|
||||||
;;;
|
;;;
|
||||||
;;; - eval-closure: A deprecated field, to be removed in Guile 2.2.
|
;;; - declarative?: a boolean flag indicating whether this module's
|
||||||
|
;;; singly-defined bindings are used in a declarative way.
|
||||||
|
;;; Declarative definitions can be better optimized by the compiler.
|
||||||
|
;;; See "Declarative Modules" in the manual, for more.
|
||||||
;;;
|
;;;
|
||||||
;;; - obarray: a hash table that maps symbols to variable objects. In this
|
;;; - obarray: a hash table that maps symbols to variable objects. In this
|
||||||
;;; hash table, the definitions are found that are local to the module (that
|
;;; hash table, the definitions are found that are local to the module (that
|
||||||
|
@ -1964,7 +1967,7 @@ name extensions listed in %load-extensions."
|
||||||
(obarray
|
(obarray
|
||||||
uses
|
uses
|
||||||
binder
|
binder
|
||||||
eval-closure
|
declarative?
|
||||||
(transformer #:no-getter)
|
(transformer #:no-getter)
|
||||||
(name #:no-getter)
|
(name #:no-getter)
|
||||||
kind
|
kind
|
||||||
|
@ -2638,6 +2641,8 @@ deterministic."
|
||||||
(nested-define-module! module name m)
|
(nested-define-module! module name m)
|
||||||
m)))
|
m)))
|
||||||
|
|
||||||
|
(define user-modules-declarative? (make-parameter #t))
|
||||||
|
|
||||||
(define (beautify-user-module! module)
|
(define (beautify-user-module! module)
|
||||||
(let ((interface (module-public-interface module)))
|
(let ((interface (module-public-interface module)))
|
||||||
(if (or (not interface)
|
(if (or (not interface)
|
||||||
|
@ -2683,6 +2688,7 @@ deterministic."
|
||||||
(define (make-fresh-user-module)
|
(define (make-fresh-user-module)
|
||||||
(let ((m (make-module)))
|
(let ((m (make-module)))
|
||||||
(beautify-user-module! m)
|
(beautify-user-module! m)
|
||||||
|
(set-module-declarative?! m (user-modules-declarative?))
|
||||||
m))
|
m))
|
||||||
|
|
||||||
;; NOTE: This binding is used in libguile/modules.c.
|
;; NOTE: This binding is used in libguile/modules.c.
|
||||||
|
@ -2830,7 +2836,7 @@ error if selected binding does not exist in the used module."
|
||||||
(define* (define-module* name
|
(define* (define-module* name
|
||||||
#:key filename pure version (imports '()) (exports '())
|
#:key filename pure version (imports '()) (exports '())
|
||||||
(replacements '()) (re-exports '()) (autoloads '())
|
(replacements '()) (re-exports '()) (autoloads '())
|
||||||
(duplicates #f) transformer)
|
(duplicates #f) transformer declarative?)
|
||||||
(define (list-of pred l)
|
(define (list-of pred l)
|
||||||
(or (null? l)
|
(or (null? l)
|
||||||
(and (pair? l) (pred (car l)) (list-of pred (cdr l)))))
|
(and (pair? l) (pred (car l)) (list-of pred (cdr l)))))
|
||||||
|
@ -2846,6 +2852,7 @@ error if selected binding does not exist in the used module."
|
||||||
;;
|
;;
|
||||||
(let ((module (resolve-module name #f)))
|
(let ((module (resolve-module name #f)))
|
||||||
(beautify-user-module! module)
|
(beautify-user-module! module)
|
||||||
|
(set-module-declarative?! module declarative?)
|
||||||
(when filename
|
(when filename
|
||||||
(set-module-filename! module filename))
|
(set-module-filename! module filename))
|
||||||
(when pure
|
(when pure
|
||||||
|
@ -3270,7 +3277,7 @@ but it fails to load."
|
||||||
((kw val . in)
|
((kw val . in)
|
||||||
(loop #'in (cons* #'val #'kw out))))))
|
(loop #'in (cons* #'val #'kw out))))))
|
||||||
|
|
||||||
(define (parse args imp exp rex rep aut)
|
(define (parse args imp exp rex rep aut dec)
|
||||||
;; Just quote everything except #:use-module and #:use-syntax. We
|
;; Just quote everything except #:use-module and #:use-syntax. We
|
||||||
;; need to know about all arguments regardless since we want to turn
|
;; need to know about all arguments regardless since we want to turn
|
||||||
;; symbols that look like keywords into real keywords, and the
|
;; symbols that look like keywords into real keywords, and the
|
||||||
|
@ -3282,53 +3289,58 @@ but it fails to load."
|
||||||
(exp (if (null? exp) '() #`(#:exports '#,exp)))
|
(exp (if (null? exp) '() #`(#:exports '#,exp)))
|
||||||
(rex (if (null? rex) '() #`(#:re-exports '#,rex)))
|
(rex (if (null? rex) '() #`(#:re-exports '#,rex)))
|
||||||
(rep (if (null? rep) '() #`(#:replacements '#,rep)))
|
(rep (if (null? rep) '() #`(#:replacements '#,rep)))
|
||||||
(aut (if (null? aut) '() #`(#:autoloads '#,aut))))
|
(aut (if (null? aut) '() #`(#:autoloads '#,aut)))
|
||||||
#`(#,@imp #,@exp #,@rex #,@rep #,@aut)))
|
(dec (if dec '() #`(#:declarative?
|
||||||
|
#,(user-modules-declarative?)))))
|
||||||
|
#`(#,@imp #,@exp #,@rex #,@rep #,@aut #,@dec)))
|
||||||
;; The user wanted #:foo, but wrote :foo. Fix it.
|
;; The user wanted #:foo, but wrote :foo. Fix it.
|
||||||
((sym . args) (keyword-like? #'sym)
|
((sym . args) (keyword-like? #'sym)
|
||||||
(parse #`(#,(->keyword (syntax->datum #'sym)) . args)
|
(parse #`(#,(->keyword (syntax->datum #'sym)) . args)
|
||||||
imp exp rex rep aut))
|
imp exp rex rep aut dec))
|
||||||
((kw . args) (not (keyword? (syntax->datum #'kw)))
|
((kw . args) (not (keyword? (syntax->datum #'kw)))
|
||||||
(syntax-violation 'define-module "expected keyword arg" x #'kw))
|
(syntax-violation 'define-module "expected keyword arg" x #'kw))
|
||||||
((#:no-backtrace . args)
|
((#:no-backtrace . args)
|
||||||
;; Ignore this one.
|
;; Ignore this one.
|
||||||
(parse #'args imp exp rex rep aut))
|
(parse #'args imp exp rex rep aut dec))
|
||||||
((#:pure . args)
|
((#:pure . args)
|
||||||
#`(#:pure #t . #,(parse #'args imp exp rex rep aut)))
|
#`(#:pure #t . #,(parse #'args imp exp rex rep aut dec)))
|
||||||
((kw)
|
((kw)
|
||||||
(syntax-violation 'define-module "keyword arg without value" x #'kw))
|
(syntax-violation 'define-module "keyword arg without value" x #'kw))
|
||||||
((#:version (v ...) . args)
|
((#:version (v ...) . args)
|
||||||
#`(#:version '(v ...) . #,(parse #'args imp exp rex rep aut)))
|
#`(#:version '(v ...) . #,(parse #'args imp exp rex rep aut dec)))
|
||||||
((#:duplicates (d ...) . args)
|
((#:duplicates (d ...) . args)
|
||||||
#`(#:duplicates '(d ...) . #,(parse #'args imp exp rex rep aut)))
|
#`(#:duplicates '(d ...) . #,(parse #'args imp exp rex rep aut dec)))
|
||||||
((#:filename f . args)
|
((#:filename f . args)
|
||||||
#`(#:filename 'f . #,(parse #'args imp exp rex rep aut)))
|
#`(#:filename 'f . #,(parse #'args imp exp rex rep aut dec)))
|
||||||
|
((#:declarative? d . args)
|
||||||
|
#`(#:declarative? 'd . #,(parse #'args imp exp rex rep aut #t)))
|
||||||
((#:use-module (name name* ...) . args)
|
((#:use-module (name name* ...) . args)
|
||||||
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
||||||
(parse #'args #`(#,@imp ((name name* ...))) exp rex rep aut))
|
(parse #'args #`(#,@imp ((name name* ...))) exp rex rep aut dec))
|
||||||
((#:use-syntax (name name* ...) . args)
|
((#:use-syntax (name name* ...) . args)
|
||||||
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
||||||
#`(#:transformer '(name name* ...)
|
#`(#:transformer '(name name* ...)
|
||||||
. #,(parse #'args #`(#,@imp ((name name* ...))) exp rex rep aut)))
|
. #,(parse #'args #`(#,@imp ((name name* ...))) exp rex
|
||||||
|
rep aut dec)))
|
||||||
((#:use-module ((name name* ...) arg ...) . args)
|
((#:use-module ((name name* ...) arg ...) . args)
|
||||||
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
(and (and-map symbol? (syntax->datum #'(name name* ...))))
|
||||||
(parse #'args
|
(parse #'args
|
||||||
#`(#,@imp ((name name* ...) #,@(parse-iface #'(arg ...))))
|
#`(#,@imp ((name name* ...) #,@(parse-iface #'(arg ...))))
|
||||||
exp rex rep aut))
|
exp rex rep aut dec))
|
||||||
((#:export (ex ...) . args)
|
((#:export (ex ...) . args)
|
||||||
(parse #'args imp #`(#,@exp ex ...) rex rep aut))
|
(parse #'args imp #`(#,@exp ex ...) rex rep aut dec))
|
||||||
((#:export-syntax (ex ...) . args)
|
((#:export-syntax (ex ...) . args)
|
||||||
(parse #'args imp #`(#,@exp ex ...) rex rep aut))
|
(parse #'args imp #`(#,@exp ex ...) rex rep aut dec))
|
||||||
((#:re-export (re ...) . args)
|
((#:re-export (re ...) . args)
|
||||||
(parse #'args imp exp #`(#,@rex re ...) rep aut))
|
(parse #'args imp exp #`(#,@rex re ...) rep aut dec))
|
||||||
((#:re-export-syntax (re ...) . args)
|
((#:re-export-syntax (re ...) . args)
|
||||||
(parse #'args imp exp #`(#,@rex re ...) rep aut))
|
(parse #'args imp exp #`(#,@rex re ...) rep aut dec))
|
||||||
((#:replace (r ...) . args)
|
((#:replace (r ...) . args)
|
||||||
(parse #'args imp exp rex #`(#,@rep r ...) aut))
|
(parse #'args imp exp rex #`(#,@rep r ...) aut dec))
|
||||||
((#:replace-syntax (r ...) . args)
|
((#:replace-syntax (r ...) . args)
|
||||||
(parse #'args imp exp rex #`(#,@rep r ...) aut))
|
(parse #'args imp exp rex #`(#,@rep r ...) aut dec))
|
||||||
((#:autoload name bindings . args)
|
((#:autoload name bindings . args)
|
||||||
(parse #'args imp exp rex rep #`(#,@aut name bindings)))
|
(parse #'args imp exp rex rep #`(#,@aut name bindings) dec))
|
||||||
((kw val . args)
|
((kw val . args)
|
||||||
(syntax-violation 'define-module "unknown keyword or bad argument"
|
(syntax-violation 'define-module "unknown keyword or bad argument"
|
||||||
#'kw #'val))))
|
#'kw #'val))))
|
||||||
|
@ -3337,7 +3349,7 @@ but it fails to load."
|
||||||
((_ (name name* ...) arg ...)
|
((_ (name name* ...) arg ...)
|
||||||
(and-map symbol? (syntax->datum #'(name name* ...)))
|
(and-map symbol? (syntax->datum #'(name name* ...)))
|
||||||
(with-syntax (((quoted-arg ...)
|
(with-syntax (((quoted-arg ...)
|
||||||
(parse #'(arg ...) '() '() '() '() '()))
|
(parse #'(arg ...) '() '() '() '() '() #f))
|
||||||
;; Ideally the filename is either a string or #f;
|
;; Ideally the filename is either a string or #f;
|
||||||
;; this hack is to work around a case in which
|
;; this hack is to work around a case in which
|
||||||
;; port-filename returns a symbol (`socket') for
|
;; port-filename returns a symbol (`socket') for
|
||||||
|
@ -4106,7 +4118,8 @@ when none is available, reading FILE-NAME with READER."
|
||||||
;; Set filename to #f to prevent reload.
|
;; Set filename to #f to prevent reload.
|
||||||
(define-module (guile-user)
|
(define-module (guile-user)
|
||||||
#:autoload (system base compile) (compile compile-file)
|
#:autoload (system base compile) (compile compile-file)
|
||||||
#:filename #f)
|
#:filename #f
|
||||||
|
#:declarative? #f)
|
||||||
|
|
||||||
;; Remain in the `(guile)' module at compilation-time so that the
|
;; Remain in the `(guile)' module at compilation-time so that the
|
||||||
;; `-Wunused-toplevel' warning works as expected.
|
;; `-Wunused-toplevel' warning works as expected.
|
||||||
|
|
|
@ -91,8 +91,6 @@
|
||||||
(define (tree-il-for-each f x)
|
(define (tree-il-for-each f x)
|
||||||
(for-each-fold x (lambda (x) (f x) (values)) (lambda (x) (values))))
|
(for-each-fold x (lambda (x) (f x) (values)) (lambda (x) (values))))
|
||||||
|
|
||||||
(define (module-conventional-bindings? mod) #t)
|
|
||||||
|
|
||||||
(define (compute-declarative-toplevels x)
|
(define (compute-declarative-toplevels x)
|
||||||
(define dynamic (make-hash-table))
|
(define dynamic (make-hash-table))
|
||||||
(define defined (make-hash-table))
|
(define defined (make-hash-table))
|
||||||
|
@ -114,15 +112,15 @@
|
||||||
(_ (values))))
|
(_ (values))))
|
||||||
x)
|
x)
|
||||||
(let ((declarative (make-hash-table)))
|
(let ((declarative (make-hash-table)))
|
||||||
(define (conventional-module? mod)
|
(define (declarative-module? mod)
|
||||||
(let ((m (resolve-module mod #f #:ensure #f)))
|
(let ((m (resolve-module mod #f #:ensure #f)))
|
||||||
(and m (module-conventional-bindings? m))))
|
(and m (module-declarative? m))))
|
||||||
(hash-for-each (lambda (k expr)
|
(hash-for-each (lambda (k expr)
|
||||||
(match k
|
(match k
|
||||||
((mod . name)
|
((mod . name)
|
||||||
(unless (or (hash-ref assigned k)
|
(unless (or (hash-ref assigned k)
|
||||||
(hashq-ref dynamic name)
|
(hashq-ref dynamic name)
|
||||||
(not (conventional-module? mod)))
|
(not (declarative-module? mod)))
|
||||||
(hash-set! declarative k expr)))))
|
(hash-set! declarative k expr)))))
|
||||||
defined)
|
defined)
|
||||||
declarative))
|
declarative))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue