I am starting a project where I will have many module files, but it seems that "send to REPL" functions are not module-aware, or am I just missing some detail?
Given the following two files:
mod1.rkt:
#lang racket/base
(require "./mod2.rkt")
(define t 1)
and mod2.rkt:
#lang racket/base
(define t 2)
I do racket-run while visiting mod1.rkt. Then I visit mod2.rkt and try to send the definition of t with racket-send-definition. It does succeed, but overrides the t from mod1.
I need to be able to re-define in a module-aware manner (i.e. the way it works in CIDER for Clojure).
Ultimately I'm aiming to develop a video game, and hot-reloading is critical for faster feedback loop, because it eliminates the need for re-starting the game and navigating to the place I was editing (restoring the internal state).
The single-module workflow seems to satisfy my needs, but there is no way to fit the whole game in a single module/file. Re-running the module seems to discard the state, which is not what I need...
Thanks for posting here -- I think you'll get better advice and explanations!
For example, I was going to reply something like: "If I were developing a game in Racket, I would start by figuring out how to persist game state -- which I'd eventually expose to players as a feature -- and that use from the start to develop, do unit tests, do perf tests, etc."
But. You should probably listen to people who have actual game development experience, not me.
p.p.s. Whatever workflow you decide on, as long as Racket supports it cleanly, I'm still glad to look at how Racket Mode could better support that workflow.
Sure. Clojure is a Lisp hosted on JVM (and other runtimes). CIDER is a Emacs package that provides integration with Clojure in a similar fashion to what racket-mode does. It also starts a REPL with which you can interact either directly from a buffer, or by sending expressions to it.
The difference is in how namespaces/modules are treated. In CIDER if I am visiting a certain file (and thus, Clojure namespace, as there is 1:1 correspondence), sending a form or expression to the currently active REPL session is namespace-aware, such that it affects the definition in that namespace.
This is what I expected from racket-mode, but it doesn't seem to work that way. Instead, the currently active module gets the definition updated, not the one I am visiting.
Racket modules are by design more static.
It allows better optimization (inlining for one).
However, all the bits and pieces are there to built a more dynamic approach.
All it takes is a bit of indirection.
You can do something like, what I linked to.
Or you can use Racket namespaces to store the functions you need to redefine.
Using your own versions of #%top and #%app you can make this seamless.
The other option is the one mentioned by Greg.
Figure out a way to make game state persistent.
If the repl in, say, racket-mode keeps an namespace for redefinitions untouched
then modules can store the game state outside the module
(whose state disappears when the module is reevaluated).
Thanks for the suggestions so far, everyone — I will definitely have a look and give them a try!
I think I owe you a slightly longer explanation of what I meant by "state" or "game state". Certainly, I am going to need to implement persistence of the game state the player cares about, such as their actual game progress, that should be able to survive restarting the game.
For now, however, I am only working on the UI framework and this is where being able to reload just the bits I actually changed is crucial for rapid development. For example, this UI features dialog windows that may pop from one another up to 5 levels deep or more. Without proper reloading, I have to go through the whole sequence of opening these windows one after another for any small adjustment I make in the code of the deepest-nested one to finally see the effects of said changes.
I actually started prototyping this game (engine) in Python, but I've outgrown its limited reloading capabilities rather quickly and was continuing purely by inertia up until now... I can share the link to the project, if there is interest and it's not against the rules here.
To get a prebuilt solution, I think the best bet is racket-reloadable,
which Greg linked to. I think, the main use case is long running servers,
but it might fit your use case too?
Video games seem to pop up quite often in the discussions around the hot-patching style of development. While I do appreciate the dangers of such style and I understand why Racket deliberately decided not to support it directly, I think some contained version thereof may be attainable and hope it will fulfill my needs for this project at least.
I am starting a project where I will have many module files, but it seems that "send to REPL" functions are not module-aware, or am I just missing some detail?
Given the following two files:
mod1.rkt:
#lang racket/base
(require "./mod2.rkt")
(define t 1)
and mod2.rkt:
#lang racket/base
(define t 2)
I do racket-run while visiting mod1.rkt. Then I visit mod2.rkt and try to send the definition of t with racket-send-definition. It does succeed, but overrides the t from mod1.
I'm not sure if this was addressed already, but Racket's REPL (assuming you sent the define lines) is one big "hopeless" namespace. It certainly supports modules, but the way that typically works is via require. You can use ,require-reloadable at a REPL to setup a reloadable module, but some things might not work well (e.g., changes to a struct will probably not work).
In Racket structures are generative as default.
Each time (struct foo (bar baz)) is evaluated, you get definitions for a new foo structure,
not compatible with earlier created foo structures.
So, if you want "reloadable" structs, you need to add #:prefab to your structure definitions.
what about letting the functions in the game code all be
stuffed away as variabes, updated by set! operations.
Then if you need to change a funvtion while debugging,
you just use set! to replace the entire function with a
new one.
Of course then you still have to keep a copy of the modified
functions in a file outside of racket so you will still have
the changes when restarting racket from scratch.
You could even make your own variation on the define macro
to create this log automatically.
The REPL is a great way to "exercise" functions by calling them. After I write some function, I might use the REPL to call it with various values. (I might even copy some of those example calls into unit tests.)
There's value in making the program consist of smaller functions, that are, well, functional -- try to avoid mutating global variables, and, can be called independently.
Applying that to your example:
I think I'd define functions to handle each of the five dialogs. That way, if I were developing the dialog that's normally called nested five deep: I could make my changes in the source file buffer, racket-run, and in the resulting REPL, I could call that function for the 5th dialog. Skip right to it.
(If that dialog depends on values from parent dialogs, hopefully it would be reasonably convenient to pass them explicitly into that function, as opposed to grabbing them from ambient global variables.)
(If the call were annoying to keep typing in the REPL, I might temporarily have it in the body of the file that I keep running... and just hopefully remember to remove that before git commit-ing .)
It's occurring to me now that, although the "send from edit buffer to REPL" command is convenient -- and a lispy tradition -- more often I do... the opposite.
Copy this example function call into a unit test.
Copy this result value into a unit test (to use as the "expected" value).
Copy this expression I worked out through trial and error in the REPL, to be a sub-expression in my edit buffer.
(Probably such a command would work best when done from some edit buffer, and "suck in" the input and/or output for the last REPL prompt. Something like that? (Heck unlike send-to-repl @EmEf might even support such a command in Dr Racket. ))
@greghendershott writes "Ialthough the "send from edit buffer to REPL" command is convenient -- and a lispy tradition -- more often I do... the opposite."
A long time ago (20 years) Eli Barzilay (@elibarzilay ) proposed something just like that to me. He observed himself and students writing tests by interacting with (simple) functions in the real and then creating them. So why not have a copy-and-paste-into-tests command in Emacs? For all I know Eli hacked this into his Emacs set-up. (Of course this defeats the purpose of "work through examples before you design a function" but hey, there's good behavior and human behavior.)