Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R7RS import and environment of load #77

Closed
tizoc opened this issue Jul 12, 2015 · 6 comments
Closed

R7RS import and environment of load #77

tizoc opened this issue Jul 12, 2015 · 6 comments

Comments

@tizoc
Copy link

tizoc commented Jul 12, 2015

Using Gauche from git (5ac6b1d from Jul 3)

When using R7RS import, calling load evaluates the code in a different environment that doesn't include the imported identifiers, but it works fine with Gauche's use:

a.scm:

(define-library (a)
  (import (scheme base))
  (export blah)
  (begin
    (define blah "blah")))

b.scm:

(display blah)

With Gauche's use:

gosh> (use a) 
#<undef>
gosh> (load "b")
blah#t
gosh>

With R7RS's import:

gosh[r7rs.user]> (import (scheme base) (scheme load))
#<undef>
gosh[r7rs.user]> (import (a))
#<undef>
gosh[r7rs.user]> (load "b")
*** ERROR: unbound variable: blah
    While loading "./b.scm" at line 1
Stack Trace:
_______________________________________
  0  blah

  1  (eval expr env)
        At line 254 of "/usr/local/share/gauche-0.9/0.9.5_pre1/lib/gauche/interactive.scm"
@shirok
Copy link
Owner

shirok commented Jul 12, 2015

b.scm isn't a proper R7RS program, so isn't interpreted as R7RS. Add (import a) at the head of b.scm. Alternatively, you can use R7RS include to include a code fragment, as opposed to a complete program, into R7RS repl.

What's happening is that load start evaluating the file with Gauche's user environment (unless specified otherwise), and counting on that the loaded file describing itself as either R7RS or Gauche. With the current spec, you can load Gauche program into R7RS repl and vice versa. (But I see this causes confusion, especially when you're on R7RS repl, so I might reconsider this.)

@shirok shirok closed this as completed Jul 12, 2015
@tizoc
Copy link
Author

tizoc commented Jul 12, 2015

Yes, include is what I replaced load with, which made me face other issues (described later).

My reasoning regarding load was as follows:

  • Unless otherwise specified, load will evaluate code inside interaction-environment (as per R7RS)
  • When in the REPL, I'm in interaction-environment, and anything imported or defined, will extend it.
  • Given these two things, code evaluated through load would both "see" all identifiers that can be "seen" in the REPL, and also extend it as a result.

Using include worked, but then I found out that the R7RS REPL is not the same as interaction-environment.

Example:

gosh[r7rs.user]> (import (scheme eval) (scheme base) (scheme repl))
#<undef>
gosh[r7rs.user]> (eval '(define in-intenv "in-intenv") (interaction-environment))
in-intenv
gosh[r7rs.user]> (eval 'in-intenv (interaction-environment))
"in-intenv"
gosh[r7rs.user]> (define in-repl "in-repl")
in-repl
gosh[r7rs.user]> (eval 'in-repl (interaction-environment))
*** ERROR: unbound variable: in-repl
Stack Trace:
_______________________________________
  0  in-repl

  1  (eval expr env)
        At line 254 of "/usr/local/share/gauche-0.9/0.9.5_pre1/lib/gauche/interactive.scm"

Using plain Gauche REPL:

gosh> (define in-repl "in-repl")
in-repl
gosh> (eval '(define in-intenv "in-intenv") (interaction-environment))
in-intenv
gosh> (eval 'in-repl (interaction-environment))
"in-repl"
gosh> in-intenv
"in-intenv"

I'm slowly getting rid of R7RSisms in my code, because I want it to work on implementations that are not nearly R7RS yet (like Guile) and others that will never be (Racket), but I wanted to mention Gauche's behaviour here because I found it surprising and may not be intended to work like that.

@shirok
Copy link
Owner

shirok commented Jul 12, 2015

Currently interaction-environment always returns #<module user>, which is intended and documented behavior. Mainly to avoid 1-argument load to be affected by the context (if you directly call load you can pass the second arg, but load may be called deep in the library you don't have control with). It wasn't an issue before, for (1) most of the time you're in #<module user> on REPL, and (2) the file to be loaded usually identifies its context by itself, either by define-library, import, or select-module.

With r7rs.user environment, it's certainly confusing interaction-environment doesn't return #<module r7rs.user>. But here's a catch---import and define-library aren't part of global bindings in R7RS; they exist rather in meta-level. So theoretically you can't load a file beginning with define-library into R7RS environment. Hmm... it seems an overlook of R7RS. I'll think over some practical compromise.

@tizoc
Copy link
Author

tizoc commented Jul 12, 2015

Ok, this cleared things a bit for me, thanks for explaining!

Switching from interaction-environment to current-module in Gauche gave me the behaviour I want for now (which is basically, a mutable environment where code can be evaluated, interaction-environment just happens to be the portable option that is guaranteed to be mutable across the various implementations).

@shirok
Copy link
Owner

shirok commented Jul 12, 2015

Beware current-module has its own catch; it's a compile-time construct. If you give up the portability, using the desired module explicitly (e.g. (find-module 'r7rs.user)) may be the safer choice.

@tizoc
Copy link
Author

tizoc commented Jul 12, 2015

Yes, saw that on the documentation, but it is fine for my use-case.

(current-module) is called only once (at program startup) and stored in a variable so that calls to eval always use that environment. It doesn't even have to be the REPL context, what matters is that it is mutable, and that it has some particular bindings available.

It looks roughly like this:

;; The code here is part of the "(shen primitives)" module
(define *shen-environment* #f)

(define (set-shen-environment! env)
  (set! *shen-environment* env))

(define (eval-in-shen expr)
  (eval expr *shen-environment*))

(define (kl:eval-kl expr)
  (eval-in-shen (kl->scheme expr)))

And on init:

;; This is the initialisation file
;; <imports and defines>
;; ...
;; set the environment
(set-shen-environment! (current-module))
;; Start the shen repl
(kl:shen.shen)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants