If "GUI.rkt"
needs identifiers from "proj.rkt"
then "proj.rkt"
needs to provide
them and "GUI.rkt"
needs to require
"proj.rkt"
, not the other way around. If the two modules need identifiers from each other then you almost certainly have a design problem.
If you want the GUI part of the program to be something that is require
d by other parts, then an obvious approach is for it to provide procedures to make things which take arguments which are things like callbacks:
(provide
...
make-main-frame
...)
(define (make-main-frame ... capture-callback ...)
(define main (new frame% [label "App"]))
(new button% [parent main] [label "Click"]
[callback (lambda (button event) (capture-callback))])
...
main)
Note, however that I don't know anything about how people conventionally organize programs with GUIs, let alone how they do it in Racket, since I haven't written that sort of code for a very long time. The basic deal, I think, for any program with modules is:
- you want the module structure of programs to not have loops in it – even if it's possible for Racket's module system to have loops, their presence in a program would ring alarm bells for me;
- where a 'lower' module in the graph (a module which is being
require
d by some 'higher' module in the graph) may need to use functionality from that higher module it should probably do by providing procedures which take arguments which the higher module can provide, or equivalent functionality to that.
The above two points are my opinion only: I may be wrong about hat the best style is in Racket.
A possible example
Here's one way of implementing a trivial GUI in such a way that the callback can be changed, but the GUI code and the implementation code are isolated.
First of all the gui lives in "gui.rkt"
which looks like this:
#lang racket/gui
(provide (contract-out
(selection-window (->* (string?
(listof string?)
(-> string? any))
(#:initial-choice string?)
(object/c)))))
(define (selection-window name choices selection-callback
#:initial-choice (initial-choice (first choices)))
;; make & show a selection window for a number of choices.
;; selection-callback gets called with the choice, a string.
(define frame (new frame% [label name]))
(new choice%
[parent frame]
[label "state"]
[choices choices]
[selection (index-of choices initial-choice)]
[callback (λ (self event)
(selection-callback (send self get-string-selection)))])
(send frame show #t)
frame)
So this provides a single function which constructs the GUI. In real life you'd probably want to provide some additional functionality to manipulate the returned object without users of this module needing to know about it.
The function takes a callback function as an argument, and this is called in a way which might be useful to the implementation, not the GUI (so in particular it's called with the selected string).
"gui.rkt"
doesn't provide any way to change the callback. But that's OK: users of the module can do that, for instance like this:
#lang racket
(require "gui.rkt")
(define selection-callback-implementation
(make-parameter (λ (s)
(printf "selected ~A~%" s))))
(selection-window "foo" '("red" "amber" "green")
(λ (s) ((selection-callback-implementation) s))
#:initial-choice "green")
Now the parameter selection-callback-implementation
is essentially the callback, and can be adjusted to change what it is. Of course you can do this without parameters if you want, but parameters are quite a nice approach I think (although, perhaps, unrackety).