guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: The Guile junk drawer and a C plea


From: Philip McGrath
Subject: Re: The Guile junk drawer and a C plea
Date: Sat, 29 Jun 2024 17:53:21 -0400
User-agent: Mozilla Thunderbird

Hi,

On 6/28/24 22:52, Thompson, David wrote:
First, I think Guile's default environment is a total mess.  It's
the very definition of a junk drawer.  There's over 1000 names in
the (guile) module!  Contrast this with R7RS-small's (scheme base)
module that only has 200ish.  Guile is an old project and I'm sure
stuff just accumulated over the years, but having so much in the
default environment makes it hard to know what a program actually
uses because many things that ought to be explicit imports are not.
This makes it a challenge to move Guile in a more "least authority"
direction.  As a rule, I think Guile should *not* add any additional
names to the default environment without an extremely good reason.
Because (guile) is imported implicitly, new names can cause clashes
with existing code that require #:replace to suppress the warning
about shadowing core bindings.  For example, the newish 'spawn'
procedure collides with 'spawn' in (goblins core) in the Goblins
project.  I think Guile needs a (multi-year, multi-major version)
plan to deprecate cruft and move the good stuff into different
modules.  Give a hoot, don't pollute (the default environment)!

On 6/29/24 06:41, Maxime Devos wrote:

The default environment is irrelevant to most Scheme libraries, since
it is irrelevant to any library defining modules with
‘define-module’, ‘define-library’ or ‘library’ forms (which is pretty
much the only reasonable way to define modules unless you are making
your own system). This already reduces a lot of compatibility
concerns. An important exception is anyone doing ‘eval’ or compiling
expressions (think anyone doing the equivalent of “guile -l
script.scm”.


Unfortunately, the "default environment" in the sense of "core bindings"
definitely is relevant to libraries using `define-module`. For example,
in this module:

```
(define-module (foo)
  #:export (foo))
(define foo
  (cons 'a 1))
```

at least the bindings for `define`, `cons`, and `quote` are implicitly
imported from `(guile)`.

R6RS libraries don't have this problem. If instead you write:

```
#!r6rs
(library (foo)
  (export foo)
  (import (rnrs base))
  (define foo
    (cons 'a 1)))
```

the bindings for `define`, `cons`, and `quote` are explicitly imported
from `(rnrs base)`. If the `import` clause were empty, you would get
unbound identifier errors. (The report specifies the meaning of
`library` and its `export` and `import` sub-forms, but it also specifies
that they are not bound by any of the libraries specified by the report,
though `library` is often implemented as a binding in some sort of
implementation-specific start-up environment that is not in scope inside
a library.)

Similarly, in the Racket module:

```
(module foo racket/base
  (provide foo)
  (define foo
    (cons 'a 1)))
```

the module's language, `racket/base`, is explicitly the source of the
binding for `provide` as well as those for `define`, `cons`, and
`quote`. If you replaced `racket/base` with `typed/racket/base` or
`lazy`, you would get a valid module with different meanings for those
bindings.

Of course, you could avoid some indentation by writing the above as:

```
#lang racket/base
(provide foo)
(define foo
  (cons 'a 1))
```

Andy Wingo's "lessons learned from guile, the ancient & spry"
(<https://wingolog.org/archives/2020/02/07/lessons-learned-from-guile-the-ancient-spry>) concludes in part:

But as far as next steps in language evolution, I think in the short
term they are essentially to further enable change while further
sedimenting good practices into Guile. On the change side, we need
parallel installability for entire languages. Racket did a great job
facilitating this with #lang and we should just adopt that.

I agree.

Obviously `#lang` does much more than this (we Racketeers tend to say `#lang` as shorthand for a bunch of complementary but mostly independent features), but I think a particularly important aspect is that each module should explicitly specify its "language"/"default environment"/"core bindings". If done well, this actually *avoids* the compatibility concerns some have raised: when you want to make a breaking change, you pick a new name, and things using the old name keep working, interoperably.

I wrote some thoughts about how Guile could gradually adopt `#lang` in a way that would provide for future compatibility in a sub-thread on this list last year: https://lists.gnu.org/r/guile-devel/2023-02/msg00075.html

My long-term pipe dream (though I have no particular plans to work on it), inspired by conversations with Christine, would be a grand, interoperable unification of Guile and Racket based on `#lang` and linklets: https://lists.gnu.org/archive/html/guix-devel/2021-10/msg00010.html and, earlier, https://lists.gnu.org/archive/html/guix-devel/2021-08/msg00099.html

Philip



reply via email to

[Prev in Thread] Current Thread [Next in Thread]