In C, you can compile with option, say, -DPROMPT
. Then, in the file you can have #ifdef PROMPT
and the code will be transformed at compile time to include things in that block.
Is there something similar in Racket? I tried racket --help
but didn't see any option that could be useful.
Yes there is, but it works a little differently than it does in C. As Alexis said, Racket compilation is semi-invisible. However, there are tools in the Racket eco system that do exactly this, the most popular being errortrace
.
Any time you use define-syntax
, you are writing a compiler plug-in, if you will. The main difference between this and C is that, in C, you are required to write these extensions in a half-baked DSL known as preprocessor directives, where as in Racket, you can write these extensions with the full power of, well, Racket.
This means that any construct you used in Racket to talk to the outside world, you can also use here. One example, as Jens suggested was to use environment variables. But since you explicitly asked for the ability to pass in arguments, you can use either current-command-line-arguments
, or better still, the command-line
module, which parses the command line arguments for you.
As an example of how this works, the following file will compile to either display the symbol 'build
, or 'no-build
, depending on if any arguments were passed on the command line:
#lang racket
(require (for-syntax syntax/parse
racket/cmdline))
(define-syntax (arguments? stx)
(syntax-parse stx
[(_)
(if (null?
(command-line
#:args args
args))
#''no-build
#''build)]))
(arguments?)
The way this differs from just having the if here without the define-syntax
is that in the first case, this happens at compile time, rather than at run time, so to speak.
This is most evident when you compile your code at a different time than when you run it, using the raco make
command.
First, lets try running and compiling at the same time:
$ racket example.rkt
'no-build
$ racket example.rkt an-argument
'build
However, now lets try compiling it before we run it, using raco make
:
$ raco make example.rkt
$ racket example.rkt
'build
See here that even though we are not passing any arguments to example.rkt
, it still returns 'build
. This is because the choice is made entirely at compile time, when raco make
is run. The call to raco make
does have 1 argument: the file example.rkt
, as such, it gets compiled simply display 'build
.
This operation can be undone by deleting the compiled file:
$ rm compiled/example_rkt.*
And re-running the example:
$ racket example.rkt
'no-build
In fact, you can even remove the example.rkt
file once it is compiled:
$ raco make example.rkt
$ rm example.rkt
$ racket example.rkt
'build
Although don't do this in general because unless you have your source backed up somewhere, you will no longer be able to retrieve it.
Finally, if you want to use the exact same -D
syntax you used in C, the command-line
has a #:multi
option which you can use to set how you would like your code to compile.
Just as one last note however, do note that compiling racket files differs from compiling C files in that the generated zo
files are transient. They will only work on the current version of the Racket VM, and since that is updated frequently (several times a year), you are much better off distributing source code. If you want to obfuscate it in a similar way to distributing C binaries, you can also distribute your fully expanded code, but that is an answer for a different question.