Racket organizes a lot of its development tooling around submodules. By sticking code inside a submodule named test
, the raco test
tool will automatically run that code. Similarly, code inside a main
submodule is run when the racket
command line tool loads a module but not when one module loads another, similar to the if __name__ == "__main__":
idiom in Python. Submodules were first introduced to Racket in this blog post, which outlines their purpose and unique behaviors quite well.
What else can we do with this approach?
When I make a server application with Racket, I usually structure it as a single Racket package. I compile the app by installing it with raco pkg install
, and I test it with raco test
. I put the server's startup script inside a main
submodule, and I bundle this all up into a Docker image. Deployment consists of building the Docker image from a Dockerfile and pushing it to an image registry, relying on some container orchestration platform somewhere to see the updated image, pull it, and restart containers.
That last step, with all the futzing about in Docker, is irritating. Particularly the Dockerfile, which is just a domain-specific language for specifying a deployment script.
Now, as a Racketeer, when I see the phrase "domain specific language" and I don't see a #lang
line, my eye starts twitching and my fingers start itching. What if Racket handled that part?
Suppose, just like we have raco test
, we also had raco deploy
. Like raco test
, this would take some form of pointers to Racket code as input (filenames, installed collection names, package names, etc.). Unlike raco test
, which scans the code for tests to execute, it would scan the code for deployment scripts. These would be represented by submodules named deploy
. So in my server app example above, next to the server's main
submodule which starts the server would be a deploy
submodule which, when run, copies the package's code into a Docker image and pushes that image to a registry.
Why do this? Well, for one thing, I could now have #lang dockerfile
! The language would use the same syntax as an ordinary dockerfile, except it would compile it into a deploy
submodule with a script that builds the docker image itself. This wouldn't even need a dependency on Docker's build system itself either, it would just need a container runtime to execute commands in. And that's in theory possible to package up as an FFI library using Racket's package manager.
Being a #lang
brings other benefits. For instance, I'd be able to reuse all of Racket's toolchain for the deployment script - no need to have docker
installed at the command line. And I'd be able to edit the file with syntax highlighting in any editor that understands Racket. And I'd be able to integrate the #lang
with Resyntax, to make suggestions about how to improve dockerfiles.
Submodules are a good idea. I think they have numerous under-explored use cases for industrial code, and a submodule-driven deployment system seems like an interesting approach. Somebody give it a shot please, unless you feel like waiting until I get some free time after the heat death of the universe.