So, just a few seconds into Sam’s “The Great Metaprogram-off” talk, and I learned something: (define-syntax (simple s) #'5)
simple
I did not expect that to work w/o parens.
@zafar.huma has joined the channel
FWIW I think “identifier macros” were introduced with syntax-case
. As far as I remember, they weren’t part of the (older) RnRs specs.
How is it possible that simple
and (simple 'a)
produce the same thing?
The macro expander in both cases call the same syntax transformer. The syntax transformer in the example receives either #’simple or #’(simple ’a) — but in the example the transformer doesn’t look at input (stx is ignored), and always returns the same output #’5.
Yes, but: (define (foo . a) 5)
(foo 'a)
(foo)
foo
Produces: 5
5
#<procedure:foo>
i.e. there’s a fundamental difference between function application and referring to the function.
Another fundamental difference is between function application, and macro invocation when expanding. :slightly_smiling_face:
Let’s say simple
is associated with a a syntax-transformer t
. The macro expander needs to do something, both when it it sees simple
(looks like a variable reference) and when it sees (simple 'a
(looks like a function call). The simplest solution is to call the same transformer - and then let the whoever wrote the transformer decided, whether different things happen or not.
I realize that @greg, but I still don’t see why simple
and (simple)
are equivalent
I didn’t mean to be flippant, I just mean that how the macro expander treats use sites needn’t be the same.
@soegaard2 actually explained what it does do
Ok, I think @soegaard2 just explained it - there’s some hand wavy stuff going on in macro expansion :)
(syntax-parse stx
[(simple . more) <standard-macro-call>]
[simple:id <looked like a variable reference>])
At this point in my understanding, I think that is a bug, not a feature though :)
I much prefer explicit over implicit, but I’m sure there must be some advantages to the implicitness that I’ll discover later.
By “bug” I guess I’m referring to the magic in define-syntax
- seeing the separate cases in the call to syntax-parse
seems fine.
I mean there is some implicit magic in having macros at all, and giving more power to the macro writer. But I see what you mean. What would you expect simple
alone to do instead? error?
Yes, I think so, given just what is shown in the example i.e. it doesn’t match.
is waiting for the upcoming, “Meta-programming in Racket” book :)
Which, by the way, would do more than Rhombus for recruiting new Racket users IMO. There. I said it.
The expressive power of Racket’s macro system is so great that, I suspect, you’d need a whole series of books, to cover the topic properly.
Possibly, but a well-written first book would do wonders, and the parens objection would be shown to be the red herring it is.
Beautiful Racket is a good book to start with.
btw I imagine the motivation was to let a macro return a function when that could make sense. You know, how maybe you try to supply a macro to a function like map
, but it doesn’t work? This makes it possible to write a macro where it could work (if that makes sense for the macro).
And, if your syntax transformer does not define that default case, you do get an error as you would like.
Very good point @kellysmith12.21, and I’m ashamed to say that, though I bought it, I haven’t actually read it yet! I’ve been so busy coding “normal” Racket, that I haven’t made the time to study the meta-programming side. I also appreciate Greg’s “Fear of Macros”. I mean, there’s a lot of great info/documentation, I don’t mean to imply there isn’t. My personality just learns better with a well structured book, in most cases. I’ll shut up until I finish “Beautiful Racket” - it may fill in the gaps I need.
(define-syntax (simple s) #'5)
is a very minimal syntax transformer that almost no one writes, ever. :slightly_smiling_face:
It does exhibit the surprise factor.
Silly example: #lang racket/base
(require (for-syntax racket/base)
(rename-in racket/base [and rkt:and]))
(define-syntax (and stx)
(syntax-case stx ()
[(_ v ...) #'(rkt:and v ...)]
[_ #'and-function]))
(define (and-function . vs)
(andmap values vs))
(and #t #f)
(apply and (list #t "There, I 'fixed' and."))
Interesting. That example is much more clear due to seeing the specific cases.
I think the confusing part is that it looks like it doesn’t “match” because the definition looks like a function, but there’s no matching happening at all. The seemingly function nature is because all macros are functions from syntax to syntax, not because this is a macro that has to be used like a function.
If you’re expecting all macros to do some pattern matching based on syntax, then you might guess that there is something there that matches an application like form, but there really isn’t.
I think the confusing part for me is having two different expressions (one w/ parens, one w/o parens) produce the same result.
i.e. if simple
produced 5
, but (simple)
produced a compile error, it would be less confusing :)
hence the surprise
You can certainly implement simple
so that it has that behavior.
Macro stepper shows: (define-syntaxes (simple) (lambda (s) 🔒(t-quote-syntax 5)))
(#%app call-with-values (lambda () (quote 5)) print-values)
(#%app call-with-values (lambda () (quote 5)) print-values)
so it appears to have expanded simple
to a function call
Although the macro stepper did not expand define-syntaxes
- if it had, the mystery may have been cleared up :)
Actually, it expanded simple
to (quote 5)
.
Because you’re using #lang racket
, all module-level expressions are implicitly wrapped to print.
Why the need for call-with-values
then?
I was referring to the invocation of simple
not to be confused with the invocation of (simple)
which is the line above ;)
here’s the full source: (define-syntax (simple s) #'5)
(simple 'b)
simple
For printing, each expression is wrapped in a thunk (the zero-argument lambda
), which is called by call-with-values
, and all the resulting values are passed to print-values
.
Yes, I skipped to the end which just confused the issue. Stepping through makes more sense.
The define-syntaxes
form was not expanded because it’s a primitive form in Racket; it can’t be expanded any further.
Ok, I just got @samth’s comment above. It took me adding a second param to experiment e.g. (define-syntax (simple s t) #'5)
:) Maybe (define-syntax (simple stx) #'5)
would’ve gotten me there sooner :)
World of difference between (define-syntax (simple s) #'5)
and (define-syntax-rule (simple s) 5)
even though they’re also very similar :)
Yes, I just used a shorter variable name because I was live coding, but stx would be clearer
event:/ui/chest_open
i need to extract “chest_open” from this string, so ill need to find the index of the last slash going backwards and substring it. Is there a built in way to do this in the standard library before i write this myself :0?
I would use string-split or regexp-split
ohhhhhhhhhhhhh
completely crossed my mind lol, yeah ill just use string split and take the last el of the list