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 ~as 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.