guix-devel
[Top][All Lists]
Advanced

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

Semantics of circular imports


From: Maxime Devos
Subject: Semantics of circular imports
Date: Sun, 20 Feb 2022 18:47:49 +0100
User-agent: Evolution 3.38.3-1

Philip McGrath schreef op zo 20-02-2022 om 11:47 [-0500]:
> I was just (or maybe am still?) dealing with some issues caused by cyclic
> imports of package modules while updating Racket to 8.4: see in particular
> <https://issues.guix.gnu.org/53878#93>, as well as #66, #112, and #113 in the
> same thread.
> [...]
> I find the semantics of Guile's cyclic module imports very confusing,
> and I don't know of any documentation for what is and isn't supported
> other than advice from Ludo’ in <https://issues.guix.gnu.org/48682#7
> —is there any?

(The following explanation ignores syntax transformers, #:select and
#:autoload.)

Basically, a module consists of a hash table and a thunk
initialising the hash table.  A few situations to demonstrate:

;; Non-cyclic
(define-module (foo)
  #:export (foo))
(define foo 0)

(define-module (bar)
  #:export (bar)
  #:use-module (foo))
(define bar (+ 1 foo))

The thunk of 'foo' would be

  (lambda ()
    (set-module-value! '(foo) 'foo 0))

and the thunk of 'bar' would be

  (lambda ()
    ;; This calls the thunk of 'foo' if it hasn't yet been called
    ;; before.
    (initialise-module '(foo))
    (set-module-value! '(bar) 'bar (+ 1 (module-value '(foo) 'foo 0))))

;; Cyclic, non-problematic
(define-module (foo)
  #:export (foo)
  #:use-module (bar))
(define foo 0)
(define (calculate-foobar)
  (+ 1 (calculate-bar))

(define-module (bar)
  #:export (calculate-bar)
  #:use-module (foo))
(define (calculate-bar) (+ 1 foo))

The initialisation thunk of 'foo' would be

 (lambda ()
   (initialise-module '(bar))  ; L1
   (set-module-value! '(foo) 0) ; L2
   (set-module-value! '(foo) 'calculate-foobar ; L3
     (lambda () (+ 1 ((module-value '(bar) 'calculate-bar)))))) ; L4

and the thunk of 'bar' is:

  (lambda ()
    (initialise-module '(foo)) ; L6
    (set-module-value! '(bar) 'calculate-bar ; L7
      (lambda () (+ 1 (module-value '(foo) 'foo))))) ; L8

Now let's see what happens if the module (bar) is loaded:

; Initialising '(bar)'
(initialise-module '(foo)) ; L6
   ;; (foo) has not yet begun initialisation, so run the thunk:
   ->  (initialise-module '(bar)) ; L1
       ;; (bar) is being initialised, so don't do anything here
   -> (set-module-value! '(foo) 0) ; L2
   -> (set-module-value! '(foo) 'calculate-foobar ; L3
        (lambda () (+1 ((module-value '(bar) 'calculate-bar)))) ; L4

      The hash table of '(bar)' does not yet contain 'calculate-bar',
      but that's not a problem because the procedure 'calculate-foobar'
      is not yet run.
(set-module-value! '(bar) '(calculate-bar) ; L7
  (lambda () (+ 1 (module-value '(foo) 'foo)))) ; L8
;; The hash table of '(foo)' contains 'foo', so no problem!
;; Alternatively, even if '(foo)' did not contain 'foo',
;; the procedure '(calculate-bar)' is not yet run, so no problem!

;; Done!

Now for a problematic import cycle:

(define-module (foo)
  #:export (foo)
  #:use-module (bar))
(define foo (+ 1 bar))

(define-module (bar)
  #:export (bar)
  #:use-module (foo))
(define bar (+ 1 foo))

Now let's reason what happens when we try importing 'bar'.
The init thunk of '(foo)' is:

  (lambda ()
    (initialise-module '(bar)) ; L1
    (set-module-value! '(foo) 'foo (+ 1 (module-value '(bar) 'bar)))) ; L2

and the thunk of '(bar)':

  (lambda ()
    (initialise-module '(foo)) ; L3
    (set-module-value! '(bar) 'bar (+ 1 (module-value '(foo) 'foo)))) ; L4

Now let's see what happens if 'bar' is loaded:

; Initialising (bar)
(initialise-module! '(foo)) ; L3
  ;; (foo) has not yet begun initialisation, so run the thunk:
  -> (initialise-module '(bar)) ; L1
     ;; (bar) is already initialising, so don't do anything
  -> (set-module-value! '(foo) 'foo (+ 1 bar)))

     Oops, the variable foo of the module (foo) has not yet been defined,
     so an 'unbound-variable' exception is raised!

I hope that illustrates a little when and when not cyclic imports work!

Greetings,
Maxime.

Attachment: signature.asc
Description: This is a digitally signed message part


reply via email to

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