3 ms·
This is cool; I've never seen an example of extending Racket's readtable. I always imagined implementing string interpolation by overriding the implicit #%datu
by arithmetization 9y ago
This is cool; I've never seen an example of extending Racket's readtable.
I always imagined implementing string interpolation by overriding the implicit #%datum macro and checking for literal strings. Here's a quick (probably non-idiomatic) example for comparison.
#lang racket
(require
(rename-in racket (#%datum core-datum))
(for-syntax syntax/parse))
(define-syntax (#%datum stx)
(syntax-parse stx
[(_ . x:str) (interpolate #'x)]
[(_ . x) #'(core-datum . x)]))
(define-for-syntax (interpolate stx)
(define re #rx"@{[^}]+}")
(define (trim match) (substring match 2 (- (string-length match) 1)))
(define (to-stx val) (datum->syntax stx val))
(define (datum val) (to-stx (cons #'core-datum val)))
(define (source text) (to-stx (read (open-input-string text))))
(let* ([text (syntax->datum stx)]
[matches (regexp-match* re text)]
[template (datum (regexp-replace re text "~a"))]
[values (map (compose source trim) matches)])
(if (null? matches)
template
(to-stx `(,#'format ,template ,@values)))))
42 ;=> 42
#t ;=> #t
"1 time" ;=> "1 time"
(define foo 2)
"@{foo} times" ;=> "2 times"
"@{(+ foo 1)} times" ;=> "3 times"
(define (scoped foo)
(define (format . args) "boom!")
"@{foo} times")
(scoped 10) ;=> "10 times"
- klibertp 9y agoGreat solution! Thanks for sharing. Your last example is especially interesting, as it shows the problem with the reader extensions I forgot to mention in the post: the introduced identifiers have local scope. In other words, both redefining `string-append` and not having it in local scope when calling it are problems. In your solution the `#'format` expression binds the `format` function to whatever is called `format` in the current module (but in the next phase, which is why you require `#%datum` normally and not `for-syntax`). I regret not putting the hygiene considerations into the post, I planned it but, honestly, forgot about it :( I'll add it to the post, along with your solution (is that alright?)
- arithmetization 9y agoI think just tweaking your parse method to use #'string-append and #'~a would be enough to preserve hygiene for your introduced identifiers. datum->syntax will happily work with mixed combinations of syntax-objects and data. And yes, feel free to do anything with that example and share further. Not enough people have had exposure to Racket's implicit expansion macros. (Is that even what they're called?)
- klibertp 9y ago> I think just tweaking your parse method to use #'string-append and #'~a Yes, but then I'd need to explain what the syntax objects really are instead of calling them "some meta-data on top of a list" and moving on :)