At-expressions
If you’ve heard of Racket “at-expressions”, maybe you think they’re “that funny Scribble notation in which you write Racket documentation.”
In fact at-expressions are a general, alternative way to write s-expressions. They can be used in various handy ways.
Let’s look at using at-expressions for a few practical things like:
- “string interpolation”
- regular expressions
- “here” strings
#lang at-exp ...
You can use the at-expression reader with a language by supplying at-exp
before the language. Examples:
1 2 |
#lang at-exp racket #lang at-exp typed/racket |
In the examples below, make sure you’re using:
1 |
#lang at-exp racket |
~a
Before we talk more about at-expressions, note that racket/format
provides the function ~a
. (~a v)
is a kind of shorthand for (format "~a" v)
, plus it offers many more formatting options.
We’ll use ~a
below.
Basic at-expressions
At-expressions are a very well thought-out system; you can read about the full syntax. For this post, we simply need to know that @func{string}
is equivalent to (func "string")
. So we can rewrite:
1 |
(~a "foo bar") ; "foo bar" |
as:
1 |
@~a{foo bar} ; "foo bar" |
(Note that ~a
is the name of the function we’re calling. The ~
has nothing to do with at-expressions; it’s part of this function’s name.)
Also special characters like \
and "
are automatically escaped:
1 2 |
@~a{A back slash \} ; "A back slash \\" @~a{"Double quotes"} ; "\"Double quotes\"" |
Inside the curly brackets, you may use @
again to “escape” to any Racket expression. For example an expression like (+ 1 1)
:
Or simply the name of a variable like x
:
1 2 |
(define x 0) @~a{x is @x} ; "x is 0" |
String interpolation
You can use at-exps as the equivalent of “string interpolation” in some other languages:
Normally in Racket you’d write that as:
Which is fine, albeit you have to flip between the ~a
s on the left and the values on the right, making sure they match up. The string interpolation style is arguably easier to write, to read, and to update later without making a mistake.
How about mixing formats, such as ~a
(display) and ~v
(print)? For example with format
we can write
How can we do this using our at-exp? Well since ~a
is the outer function it will display the value of any ~v
inside. Remember that @
lets us “escape” to any Racket expression, not just a variable, it could be a function application. So:
You can also surround the Racket expression in |
characters. This is useful if the expression needs to end next to plain text. You can demarcate the identifier from the text:
1 |
@~a{x is @|x| and y is @|y|!} ; "x is 0 and y is foo!" |
The |
keeps !
from being read as part of the identifier y
.
Regular expressions
Do you enjoy writing regular expressions like #px"\\d\\.\\d"
? Me neither.
Another useful example is avoiding the need to use \\
to get a \
in string literals. This is especially handy for regular expressions:
1 |
@pregexp{\d\.\d} ; #px"\\d\\.\\d" |
If you find pregexp
too verbose, you could define a little alias:
“Here” strings
Like shells, Racket has “here” strings:
1 2 3 4 5 6 |
Cool. However the indentation is tricky. You get extra spaces if you do this:
1 2 3 4 5 6 |
Oops.
Also the EOF
must be alone on a line and in column 0. You can’t let that get indented, and you can’t put the closing paren on the same line.
At-exps are more elegant and survive typical re-indentation:
1 2 3 |
How to write a literal @
If @
is a magic escape character, how do you write a literal @
?
-
We want a string,
"@"
. -
How do we escape to any Racket expression, including (say) a string? Using
@
. -
Therefore prepend a
@
to"@"
:
1 |
@"@" |
So for example:
1 |
@~a{The email is foo@"@"bar.com} ; "The email is foo@bar.com" |
Conclusion
This was a quick look at some practical ways to use at-expressions for more than writing Scribble documentation. Again, feel free to read up on the full syntax.