module instance static?

44 views
Skip to first unread message

Thomas Lynch

unread,
Sep 23, 2015, 4:46:43 AM9/23/15
to Racket Developers

There is a module that is utilitarian in nature, that is included in another other modules.   These other modules are in turn included in yet other modules.  

module A:

    #lang racket
     (require  "utility.rkt")


module B:

     #lang racket
     (require "utility.rkt")


module C:

     #lang racket
     (require "A.rkt")
     (require "B.rkt")


'utility' has some initialization code and a module variable that is intended to be shared by the utility code.  Say for the sake of example,
utility maintains a collection of test functions:

(define test-routines '())
(define (test-name a-test) (symbol->string (object-name a-test)))
  
(define (test-hook a-test)
(display "hooking test: ") (displayln (test-name a-test))
(set! test-routines (cons a-test (remove a-test test-routines (λ(e f) (string=? (test-name e) (test-name f)))))))

(define (test-all)
    (let*(
          [results (map test (reverse test-routines))]
          [no-tests (length results)]
          [result-flags (map cadr results)]
          [error-count (count (λ(e) (not e)) result-flags)]
          [all-passed (andmap (λ(e) e) result-flags)]
          )
      ;;(displayln results)
      (cond
        [all-passed (display "all ")(display no-tests) (displayln " passed")]
        [else 
          (display "failed: ") 
          (display error-count)
          (display " of ") 
          (displayln no-tests)
          ]
        )
      all-passed
      ))

So what is happening here is that the module code initializes a global test list.  Other modules which require utilities.rkt  then call  test-hook to hook in their test code.  Then after all the modules are loaded, the application calls  (test-all)  to see that everything is running well.
 
However, it isn't working because the "utility.rkt" module initialization code is running multiple times, and each time clobbering (making new version?) of the test list.   This behavior did not occur when the require call depth was only one deep, but now that it is two deep, it is happening.

racket@> (enter! "object.rkt")
hooking test: example-pass-test

...
(test-all) to run the tests
hooking test: and-form-test-0
hooking test: test-Λ-0
...
===>  RUNNING INIT CODE IN MODULE A SECOND TIME: 
hooking test: example-pass-test
...
hooking test: test-flatten-1
hooking test: test-replace-0
         .... 
===>  RUNNING INIT CODE IN MODULE A THIRD TIME: 
hooking test: example-pass-test
hooking test: example-fail-test
....

Should it be like this?   Is a module static (one copy)  or is it like a class (new copy on invocation)  actually I'm seeing a third behavior, static at one level deep, new instances at two levels deep (or just running the code again over the same instance?)




Robby Findler

unread,
Sep 23, 2015, 7:44:31 AM9/23/15
to Thomas Lynch, Racket Developers
The top-level code in the module runs only once. Here's a small
version of your code and the results of running it.

☕ cat A.rkt
#lang racket
(require "utility.rkt")
(printf "A\n")
☕ [robby@gongguan] ~/x
☕ cat B.rkt
#lang racket
(require "utility.rkt")
(printf "B\n")
☕ [robby@gongguan] ~/x
☕ cat C.rkt
#lang racket
(require "A.rkt")
(require "B.rkt")
(printf "C\n")
☕ [robby@gongguan] ~/x
☕ cat utility.rkt
#lang racket
(printf "utility\n")
☕ [robby@gongguan] ~/x
☕ racket
Welcome to Racket v6.2.900.17.
-> (enter! "C.rkt")
utility
A
B
C
"C.rkt">
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-dev+...@googlegroups.com.
> To post to this group, send email to racke...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-dev/421e6cbe-e3f4-4164-8cff-db74187bd986%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Thomas Lynch

unread,
Sep 23, 2015, 10:56:33 AM9/23/15
to Robby Findler, Racket Developers
So that is the defined behavior, module code is to run only once independent of the shape of the require tree?

Yeah, that output shown above is coming from the program, cut from the transcript, so I'm scratching my head as to how the module code is running more than once.  I'll stub down the actual code...  ah I have an idea!   Maybe in some cut and paste jobs I made more than one copy of it ... hmmm

Robby Findler

unread,
Sep 23, 2015, 11:06:47 AM9/23/15
to Thomas Lynch, Racket Developers
On Wed, Sep 23, 2015 at 9:56 AM, Thomas Lynch <thomas...@reasoningtechnology.com> wrote:
So that is the defined behavior, module code is to run only once independent of the shape of the require tree?


At phase 0, yes.
 
Yeah, that output shown above is coming from the program, cut from the transcript, so I'm scratching my head as to how the module code is running more than once.  I'll stub down the actual code...  ah I have an idea!   Maybe in some cut and paste jobs I made more than one copy of it ... hmmm


That can happen. :) It wouldn't be the first time!!

Robby

Thomas Lynch

unread,
Sep 24, 2015, 11:46:00 PM9/24/15
to Racket Developers
Darn, It is not due to copy and paste, but rather due to a module in the require chain using require for syntax. Details and questions follow.

So here we enter extentions-lib.  This causes the test-lib to be initialized three times:

racket@> (enter! "extentions-lib.rkt")
"intializing test-lib.rkt module"
"intializing test-lib.rkt module"
"intializing test-lib.rkt module"

extension-lib.rkt:
#lang racket

  (require "test-lib.rkt")
  (require "arith-lib.rkt")
  (require (for-syntax "arith-lib.rkt"))

arith-lib.rkt: 
#lang racket
  (require "test-lib.rkt")

test-lib.rkt:
#lang racket
"intializing test-lib.rkt module"

How is it that test-lib.rkt is initialized three times?  I get two times, the 'for syntax' time and the regular time.

Going back and looking at the actual program, it does seem that the test list available for invocation at run time is the correct one, and the interspersed require for syntax inits did not share state.   However, the transcript is messed up, because the messages from all the inits are dumped in, independent of the phase of the initialization.  I'm going to need a 'this is not phase zero'  flag and conditional behavior for the test code.  How is that done?

Also interesting apparently phase 1 is not a distinct phase that occurs before phase 0, but rather it is jumping back and fourth.  Is that correct?


Robby Findler

unread,
Sep 25, 2015, 6:56:15 AM9/25/15
to Thomas Lynch, Racket Developers
The number of times that a phase 1 module is required is not easy to
predict because it depends on how the program is run. But yes, state
is not shared unless the state is external (network connections, IO
(like you see here), files in the file system).

Robby
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-dev+...@googlegroups.com.
> To post to this group, send email to racke...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-dev/09063244-078b-468c-ae76-856696eaae47%40googlegroups.com.

Thomas Lynch

unread,
Sep 25, 2015, 8:07:10 AM9/25/15
to Racket Developers, thomas...@reasoningtechnology.com
Interesting thing here, test-lib.rkt  was never explicitly called from phase 1.    (There are no (require (for-syntax "test-lib.rkt") statements in the code.  Yet it must be written to behave properly in phase 1 (2, 3 ...).   So it seems all code that uses external resources must manage them accordingly.


Robby Findler

unread,
Sep 25, 2015, 8:57:41 AM9/25/15
to Thomas Lynch, Racket Developers
Yes, and mostly we do that at phase 0 (compilation doesn't generally
make network connections (although it could)) so we get the help from
the runtime system that we discussed earlier.

Robby
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-dev+...@googlegroups.com.
> To post to this group, send email to racke...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-dev/678b9f34-3410-4799-87cf-27e2c2758dd8%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages