@soegaard2 FWIW, here’s my lengthy experience working on the tutorial: https://pastebin.com/raw/65CgwMp2
@jjlammi has joined the channel
@anything Thanks! This is great feedback - and has given me some food for thought.
There were a few questions, so here are some thoughts:
In the experiment in section 5.1, you add a new form and calls read-syntax twice. That does indeed give you two syntax objects. You observe correctly, that there is no binding information.
The reader only reads the syntax objects (and stores the source location information). Later, the expander analyses the objects and adds binding information. So, in your example the expander haven’t run yet.
The section with “third, fourth and fifth lines” could be improved. I’ll see if I can add line numbers. If not, maybe I should repeat the lines?
You are now, the second suggesting some examples earlier. I need to think about how that would change the flow.
Your comment, that with-syntax
is like let
is spot on.
Normal variables are bound with let
.
Pattern variables are bound with with-syntax
.
It’s rare in Racket other types of variables, so I agree it feels a bit odd.
They are used to make the templates easier to use.
If we want to write a template like (syntax (let ((x 1)) (+ x 1))
. Here there are three identifiers: let, x and +. If these were normal variables, then we need some way to say “insert the name x” or “insert the value of x”. With standard list syntax, we would use `(let ((,x 1)) (+ ,x 1))
to say that we want literal let
and +
, but the value of x
(which could contain some other name).
The solution here is to mark every “pattern variable” in the template with a comma.
If a pattern variable is used multiple times, it needs to be marked every, single time. Forgetting one leads to an error.
On the other hand, with with-syntax
we can “bind x as a pattern variable” once, and then use x multiple times in a template.
Then the form (syntax ...)
will automatically treat every instance of x the same way.
As a plus, if you want to test a subexpression of a template, you can simple copy-paste it to the repl and try it. With the other method, one has to remove all the commas.
Your observation that begin
is needed when a template needs to produce multiple definitions are correct. There is similar begin
in the silly example in section 2, but I think I’ll make “multiple definitions” example in a later section.
> In section 7 I realize that what you’re doing is > very interesting: you’re manipulating objects at run-time. That’s a > way to test what we want macros to do — but in a laboraroty. I > decided to go back to section 6 again for a new look with this thought > in mind. That’s an important point. I’ll try to make it clearer.
The example without ~@ : Yeah - I think, I’ll remove it again. Wasn’t too sure about the relevance, when I wrote it.
Sorry about the mistake in the exercise. It was meant to be:
(define result0
(with-syntax ([(num ...) (syntax (1 2 3))])
(syntax ???-template)))
(equal? (syntax->datum result0)
(list 1 2 3))
And then the solution becomes, (define result0
(with-syntax ([(num ...) (syntax (1 2 3))])
(syntax (num ...))))
(equal? (syntax->datum result0)
(list 1 2 3))
I’ll need to go over the other exercises as well.
Wrt to your example, I suggest: (define methods '(web3_clientVersion
web3_sha3))
(define hyphenated-methods
(map underscore-camel->hyphens methods)
(define (try4)
(with-syntax ([(m ...) hyphenated-methods])
(syntax (begin (define m 1) ...))))
Note that the symbols must be rewritten before you use them in the template.
Alternatively: (define methods '(web3_clientVersion
web3_sha3))
(define (try5)
(with-syntax ([(m ...) (map underscore-camel->hyphens methods)])
(syntax (begin (define m 1) ...))))
Note that, if you want to use the example in macro, the list of methods needs to be available at compile time. Use begin-for-syntax
to do that: (begin-for-syntax
(define methods '(web3_clientVersion
web3_sha3))
(define hyphenated-methods
(map underscore-camel->hyphens methods))
(define (try4)
(with-syntax ([(m ...) hyphenated-methods])
(syntax (begin (define m 1) ...))))
Thanks again for the feedback.
hey, what’s the correct racket incantation to run a module and then embed yourself in a repl after evaluation (similar to drracket)
Thank you!
When I add the missing rules of the ancestors to definition by hand, it works! See the definition lines 7 to 11 and results on REPL. However, it does not anything if REPL is used for entering the code lines, i.e. everything must be given as definitions and run as usually.
The problem with the repl is a bug, if you use a snapshot (see http://snapshot.racket-lang.org\|snapshot.racket-lang.org) starting tomorrow it’ll be fixed