jsn
2019-3-6 16:21:22

https://docs.racket-lang.org/pollen/second-tutorial.html?q=pollen I am having issues with the tutorial, end of section 6.6.4. The following snippet “template.html.p” results in an error when trying to render the file “article.html” as described in the tutorial. The issue seems to be with the first case of when/splice. <html> <head> <meta charset=“UTF–8”> <title>◊(select ’h1 doc), by MB</title> <link rel=“stylesheet” type=“text/css” href=“styles.css” /> </head> <body>◊(->html doc) The current page is called ◊|here|. ◊(define prev-page (previous here)) ◊when/splice[prev-page]{The previous is <a href=“◊|prev-page|“>◊|prev-page|</a>.} ◊when/splice[(regexp-match “article” (symbol->string (next here)))]{The next is <a href=“◊|(next here)|“>◊|(next here)|</a>.} </body> </html>

Have any experienced similar? Of the three documents included in the example, arcticle.html.pmd results in a lenghty error text. Exception The application raised an exception with the message: pollen: Can’t convert procedure #<procedure:pollen-tag:here> to string @mbutterick


lexi.lambda
2019-3-6 16:33:16

@jsn Do you get the same error if you try to render just this file? &lt;html&gt; &lt;head&gt; &lt;meta charset="UTF-8"&gt; &lt;title&gt;◊(select 'h1 doc), by MB&lt;/title&gt; &lt;link rel="stylesheet" type="text/css" href="styles.css" /&gt; &lt;/head&gt; &lt;body&gt;◊(-&gt;html doc) The current page is called ◊\|here\|. &lt;/body&gt; &lt;/html&gt;


jsn
2019-3-6 18:44:39

thanks @lexi.lambda it seemed to be some issue with the index.ptree. I can’t reproduce the error after I stopped the server: raco pollen reset and raco pollen start


slack1
2019-3-6 21:24:36

Do people typically use match for destructuring a list into function arguments?


soegaard2
2019-3-6 21:26:18

Depends. Since optional arguments got support the use for that purpose has declined.


slack1
2019-3-6 21:29:43

So let’s say I have (define (quadratic a b [c 0]) (calculations))


slack1
2019-3-6 21:30:09

And someone had a list of arguments (define args '(1 1 5))


slack1
2019-3-6 21:30:46

How would optional arguments interact with my goal?


notjack
2019-3-6 21:31:09

What’s your goal?


soegaard2
2019-3-6 21:31:12

You could either write (apply quadratic args) or


soegaard2
2019-3-6 21:31:45

write (define (quadratic* . args) (apply quadratic args)) and then use (quadratic* args).


slack1
2019-3-6 21:33:12

ah I see thanks


slack1
2019-3-6 21:33:17

so much simpler


slack1
2019-3-6 23:25:20

Is there string interpolation in Racket? Is that something people look for?


notjack
2019-3-6 23:27:38

@slack1 people sometimes use at-expressions and the formatting functions of racket/format to do that


slack1
2019-3-6 23:28:10

ah I see


notjack
2019-3-6 23:28:17

greg has a blog post describing the technique: https://www.greghendershott.com/2015/08/at-expressions.html


slack1
2019-3-6 23:28:21

I was slightly avoiding the racket/format because they all seemed positional


slack1
2019-3-6 23:30:39

Are at-exp a “core” part of Racket even though it’s like a separate thing?


notjack
2019-3-6 23:31:17

It’s part of the standard library, though it’s mostly used for writing Scribble docs


slack1
2019-3-6 23:32:13

I see, it’s pretty nice


slack1
2019-3-6 23:32:49

thanks


bkovitz
2019-3-7 00:33:04

@slack1 I’ve been using at-expressions lately for just about everything. For example:


michaelmmacleod
2019-3-7 01:16:11

lexi.lambda
2019-3-7 01:42:19

@michaelmmacleod Macros that expand to submodules are a bit strange. The problem is that normally, a module has its own, totally self-contained scope—it can’t refer to bindings outside itself unless they are explicitly imported with require. But in a submodule, the syntax objects that make up the module already belong to a scope—specifically, the scope of the enclosing module.

One thing the expander could do is throw all the scoping information away, so that the bindings in the enclosing module are completely irrelevant, but it doesn’t. I can’t immediately tell you why. (Maybe there’s a reason, but I don’t know it off the top of my head.) Instead, the expander keeps the scope of everything, but just makes the bindings from the enclosing module inaccessible. This is what is happening in your program: your + is referring to the + in the enclosing module due to hygiene, but that binding isn’t available in the submodule. Instead, there’s a separate + binding that comes from the racket module language of the tester submodule, but your + doesn’t refer to it.

So what can you do? Well, one thing to do is to throw away all that extra scoping information yourself. You can do that using strip-context from the syntax/strip-context module. This program works:

#lang racket

(require (for-syntax syntax/strip-context))

(define-syntax (testing stx)
  (syntax-case stx ()
    [(_ form ...)
     #`(module . #,(strip-context
                    #'(tester racket form ...)))]))

(testing (+ 2 3))

(You can’t apply strip-context to the module part because the module identifier needs to be bound in the enclosing module, since that’s what initiates a submodule in the first place.)

Another thing you can do is tweak the scope of the submodule’s module language. It turns out that the expander makes the submodule’s bindings available using the scope of the identifier used as the module language, in this case the literal identifier racket. So if you unhygienically tweak that identifier’s scope to be the same as the syntax passed into the macro, then the bindings will become accessible again:

#lang racket

(define-syntax (testing stx)
  (syntax-case stx ()
    [(_ form ...)
     #`(module tester #,(datum-&gt;syntax stx 'racket)
         form ...)]))

(testing (+ 2 3))

One final option would be to use the special #f module language instead of using racket at all. This is only allowed with module* submodules, but it circumvents everything I said at the beginning of this message. A module declared with the #f module language doesn’t get its own scope with its own set of bindings at all, and instead it just inherits the scope of the enclosing module. Therefore, if you do this, your macro will work as-is, since the outer + binding will still be available inside the submodule:

#lang racket

(define-syntax (testing stx)
  (syntax-case stx ()
    [(_ form ...)
     #`(module* tester #f
         form ...)]))

(testing (+ 2 3))

Of course, using #f for the module language means that the enclosing module’s bindings will always be used, instead, so if the outer module was not written in #lang racket the submodule wouldn’t be, either. That may or may not be what you want, I don’t know.


michaelmmacleod
2019-3-7 02:09:32

Thanks @lexi.lambda for the detailed response! I’m going to go with the strip-context approach, because the macro I’m writing also requires other modules, i.e., racket/unsafe/ops, so that form ... can use their bindings, i.e., unsafe-fl+.


lexi.lambda
2019-3-7 02:10:44

If you use the datum-&gt;syntax trick on the other module names (i.e. do (datum-&gt;syntax stx 'racket/unsafe/ops)), then that approach would work for that, too. Whether that is better or worse than the approach using strip-context, I don’t know.


lexi.lambda
2019-3-7 02:11:26

Maybe @mflatt knows why submodules don’t do the context-stripping automatically, but he isn’t in this channel. I guess I’ll try in #general.


michaelmmacleod
2019-3-7 02:13:05

I tried using (require #,(datum-&gt;syntax stx 'racket/unsafe/ops)), but the expander reported that the require form identifier’s binding was ambiguous.


lexi.lambda
2019-3-7 02:15:09

@michaelmmacleod This code works for me, but maybe I’m overlooking something that could go wrong in bigger programs: #lang racket (define-syntax (testing stx) (syntax-case stx () [(_ form ...) #`(module tester #,(datum-&gt;syntax stx 'racket) (require #,(datum-&gt;syntax stx 'racket/unsafe/ops)) form ...)])) (testing (unsafe-fx+ 2 3))


lexi.lambda
2019-3-7 02:17:02

Also, I think I changed my mind. I don’t need to bother Matthew; I think I see why it makes sense that submodules don’t strip scopes automatically. Technically, ordinary modules don’t strip any scopes, either, which is what enables the horrible hack of #lang readers sticking scopes on syntax objects to work. I don’t know if this is actually a good rationale for the current behavior, but it’s at least consistent. :stuck_out_tongue:


michaelmmacleod
2019-3-7 02:18:02

Oh, I think the problem is that my testing was defined in a different file from where I used it. If you split the example up into two files, it should give you the ambiguity error. For example, #lang racket (provide testing) (define-syntax (testing stx) (syntax-case stx () [(_ form ...) #`(module tester #,(datum-&gt;syntax stx 'racket) (require #,(datum-&gt;syntax stx 'racket/unsafe/ops)) form ...)])) #lang racket (require "testing-macro-file.rkt") (testing (unsafe-fx+ 2 3))


lexi.lambda
2019-3-7 02:20:36

Ah, I see the error now on my end, too. It’s subtle. The fact that it exists probably means you shouldn’t bother with the datum-&gt;syntax approach, yeah.


lexi.lambda
2019-3-7 02:21:46

The issue, basically, is that since you changed the binding of the racket module language, now the require that comes from inside the macro can’t properly access the right binding. Oops.


lexi.lambda
2019-3-7 02:22:37

I think the strip-context approach is probably less evil, anyway, so go with that. :stuck_out_tongue:


michaelmmacleod
2019-3-7 02:23:01

Yeah, it seems like a much nicer approach


lexi.lambda
2019-3-7 02:24:36

There is a small downside to the strip-context approach as-written that I didn’t mention, which is that DrRacket’s Check Syntax (and, by extension, racket-mode’s binding-aware features) will stop working on the forms inside a use of the testing macro.


lexi.lambda
2019-3-7 02:24:54

But you can fix that by coupling strip-context with syntax-local-introduce. I don’t think doing that should cause any problems.


lexi.lambda
2019-3-7 02:29:18

This version makes Check Syntax/background expansion happier: #lang racket (require (for-syntax syntax/strip-context)) (define-syntax (testing stx) (syntax-case stx () [(_ form ...) #`(module . #,(syntax-local-introduce (strip-context #'(tester racket form ...))))])) (testing (+ 2 3)) (There are still technically some very, very minor things weird about doing that, but I can’t immediately think of how to resolve them, and I doubt most people who aren’t me would even notice.)


michaelmmacleod
2019-3-7 02:35:56

Thanks for the syntax-local-introduce tip, now I can get my lovely binding arrows :slightly_smiling_face: