
@matias I found eventually in the dock. But I am not used to use the dock. To conserve horizontal space I have hidden the dock and have a 90 sec delay for it to appear.

@philip.mcgrath You need define-rename-transformer-parameter (see the syntax parameter docs for why).

@lexi.lambda Thanks! This is getting me closer (I guess it makes sense that class
would be using syntax-local-value
internally). I can put syntax-parameterize
around a single expression in the class body, but I’m still struggling to syntax-parameterize
the whole body (or, at least, multiple definitions and expressions). If I put syntax-parameterize
outside of class
, I get an unbound local member name
error (though in terms of arg
now, not lexical
), I would guess because it gets extra scopes from being bound as a class-internal name, but if I use splicing-syntax-parameterize
inside the class (after the relevant init
), I still get identifier used out of context: lexical
.

@philip.mcgrath Looks like a bug to me. Peeking at the class
form source code and at its expansion in the macro stepper, it looks like what’s going on is that class
doesn’t actually expand expressions itself: it expands to some code with the unexpanded expressions left behind and lets the macroexpander do the work. So when you write (class object%
(super-new)
(init [lexical 1])
(define-syntax indirect (make-variable-like-transformer #'lexical))
(printf indirect))
it gets turned into something like (define-syntax indirect (make-variable-like-transformer #'lexical))
(letrec-syntaxes+values ([(lexical) (make-init-redirect #'lexical2)])
([(lexical2) unsafe-undefined])
(begin
(super-new)
(set! lexical2 (extract-args init-args 'lexical))
(printf indirect)))
Notice that the define-syntax
ends up getting moved outside the letrec+syntaxes+values
where lexical
is rebound.

I think the right fix is to change class
so that it expands expressions in the same context it expands everything else, but an easy quick fix that should solve your problem would be to move the define-syntax
inside the letrec-syntaxes+values
so that things get bound properly.

Then you should be able to use splicing-syntax-parameterize
and things should work.

An aside: the way the class
form handles local member names is, in general, a little fishy. It appears to simply find the member names that were bound and unhygienically introduces new bindings that shadow those names. I think a better way would be to make local member names work via syntax parameters instead of shadowing, but I wouldn’t be surprised if class
predates syntax parameters, hence the current implementation strategy.

The problem with the existing implementation strategy is that a macro that expands into a local member name might not be captured by the introduced init-redirect
binding, which is probably not what people would expect. This is why you need the syntax-parameterize
to be inside the class body, not outside it—the RHS of the syntax parameter needs to end up getting the local scopes of the class body, otherwise it won’t be shadowed.

If I were to call execv
via the FFI, would that break things in particularly bad ways, or should it be reasonably safe (modulo general FFI unsafety)? Might there be something I ought to call before calling execv
?

Has anyone noticed that OpenGL contexts for Racket don’t seem to work on Windows with Intel graphics cards?

(They do seem to work with nvidea ones, and on os x and linux)

You just get a blank window: https://stackoverflow.com/questions/54935910/how-to-make-a-programmable-pipeline-opengl-context-in-racket

(Based on git blame win32/gl-context.rkt
, it sounds like @mflatt would either be the right person to talk to, or know the right person to talk to?)

There are problems. Some file descriptors might be left open, and some signal handlers or dispositions might be different from what they should be for a new process. It might also create some problem to skip some shutdown actions, such as call plumber-flush-all
on the original plumber or some custodian callbacks, but those effects are no worse than the state after crashing.

Are the things you mentioned (signal handlers, etc.) properly adjusted for subprocesses spawned with subprocess
, or do they inherit those things, too? (I’m okay with file descriptors being left open, since if they’re not marked close-on-exec
, they’d be left open anyway.)

Those things are adjusted for the new process created by subprocess
. (Otherwise, subprocess
would be broken.)

Got it. And I imagine it would be effort (maybe big and maybe small, but definitely nonzero) to make it possible to perform the kinds of adjustments subprocess
and the various process shutdown handlers perform without actually spawning a process/the VM down so that the process can safely call execv
?


@lexi.lambda Thanks yet again—that looks promising! I suspected class
was doing something wrong, but … well, (define-syntaxes (class* _class class/derived)
<1434 lines of code here>)
is a great example of why syntax-parse
etc. are such a big step forward for macro writers. I will open an issue to track this and try the quick fix.

For local member names in general, I would want the behavior you describe, but I’m a bit fuzzy on whether the current behavior is intentional. I had figured out at an earlier stage of trying to debug this that class
, as you said, “appears to simply find the member names that were bound and unhygienically introduces new bindings that shadow those names.” I’ve read https://docs.racket-lang.org/reference/createclass.html#(part._extnames) a few times now, and these parts raise questions for me:

“If a single id
is provided for a method declaration, the identifier is used for both the internal and external names.”

“Each init
, init-field
, field
, or inherit-field
variable similarly has an internal and an external name. The internal name is used within the class to access the variable, while the external name is used outside the class when providing initialization arguments …, inheriting a field, or accessing a field externally ….”

“A single identifier can be used as an internal identifier and an external identifier, and it is possible to use the same identifier as internal and external identifiers for different bindings. … Overall, each internal identifier must be distinct from all other internal identifiers, each external method name must be distinct from all other method names, each external field name must be distinct from all other field names, and each initialization argument name must be distinct from all other initialization argument names.”

“By default, external names have no lexical scope … The define-local-member-name
and define-member-name
forms introduce scoped external names.”

(Note that it doesn’t specify a predicate for its notion of “the same identifier” and “distinct” identifiers.)

I could see an interpretation of those passages—and I hope this is wrong, because I don’t want it to work this way—to say that (init [x 1])
is really just shorthand for (init [(x x) 1])
. The external identifier is a reference—either to something bound in the “member namespace” or to a global, if there is no such binding—and the internal identifier identifier is a binding occurance that just happens to be symbolically the same as the external identifier. So, under this interpretation, a macro that expands to a local-member-name use is specifically referring to a “scoped external name,” which shouldn’t refer to the symbolically-identical internal name.

As I said, though, I don’t like this behavior. Even if this is what the documentation is telling us to expect (and it certainly isn’t doing so clearly and unambiguously), I’m having a hard time coming up with a scenario when that behavior would actually be useful, where I would have liked the local-member-names-as-syntax-parameters behavior. I would support support changing this even if it were technically “breaking” behavior that the documentation had been attempting to specify, as long as nothing is actually relying on the current behavior in practice (which seems very unlikely).

@soegaard2 I don’t know who that guy is - but he should be writing books - I like the clarity of his writing.

:wave::skin-tone–3: anyone knows if this is possible to use response
from web-server/http/response-structs
in typed racket? I struggle with that…

@pierre I believe you would need to use require/typed
to tell Typed Racket about its type. Have you tried that?

i tried using (require/typed 'response-wrapper
[#:opaque response response?])

but this does not work as expected

Using #:opaque
allows you to work with responses as opaque values. You can accept them as arguments, pass them around, and recognize them with response
—but it doesn’t tell Typed Racket how to construct responses or access their fields. To do that, you must use a #:struct
clause with require/typed
.

I just want to pass them through actually. Only part of my code is typed. Looking at #:struct
:eyes: …

If you’re just passing them through, then what problem are you running into with #:opaque
?

Here’s a first attempt with #:struct
:

(require/typed
web-server/http
[#:struct header
([field : Bytes]
[value : Bytes])]
[#:struct response
([code : Number]
[message : Bytes]
[seconds : Number]
[mime : (U #f Bytes)]
[headers : (Listof header)]
[output : (-> Output-Port Any)])])
But note two issues: 1. The result of output is really AnyValues
, but Typed Racket can’t generate a contract for that. Since in practice only the library calls output
, this isn’t likely to cause problems in practice. 2. The docs say that code
and message
have the contract number?
, but they should be more restrictive (e.g. exact-positive-integer?
for code).

indeed, after some more fiddling around my signature, the error went away… :neutral_face:. I’m reloading code and had probably some stuff stuck in memory that was not correct

:thumbsup: amazing, thanks for that :slightly_smiling_face: I’ll definitely use this as a starting point

I’m in the process of migrating a small webapp to typed racket and this is both exciting and challenging. I’m a casual racketter and my typed-racket is really :cow: for now…

Yes – probably not too bad, but some effort. I can add it to my list.

That would explain the weird error messages I was getting…

It’s okay, it isn’t very important. I’d like to have it, but it’s very low priority. :)

@pierre typed racket comes with types for the header
and response
structs. They’re available by (require typed/web-server/http)
. The docs are sparse but anyway: https://docs.racket-lang.org/ts-reference/Libraries_Provided_With_Typed_Racket.html#(mod-path._typed%2Fweb-server%2Fhttp)

I have a semi-naive question here. I have a student who passed (lambda (x) x)
to local-expand
and got back (lambda (x) x)
rather than (for example) #%plain-lambda
. Am I right in saying that this is because lambda
is a core syntactic form, and so it obeys this rule in the “Expansion steps” documentation: >Thus, the possibilities that do not fail lead to an identifier with a particular binding. This binding refers to one of three things: > … A core syntactic form, which is parsed as described for each form in Syntactic Forms.

@krismicinski The devil is in the details: just because both the input and output identifiers are named lambda
does not mean they are the same lambda
. Indeed, just because they aren’t spelled #%plain-lambda
doesn’t mean they aren’t actually the same binding.

DrRacket can tell you what it thinks the actual binding of a syntax object is if you print it and click on the little triangle that shows up in the interactions window.

No, it is because it says in https://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29 to “Beware that the symbolic names of identifiers in a fully expanded program may not match the symbolic names in the grammar. Only the binding (according to free-identifier=?
) matters.” You can demonstrate this with the following program: #lang racket
(require syntax/parse/define)
(define-syntax-parser try-local-expand
[(_)
(define expanded
(local-expand #'(lambda (x) x)
(syntax-local-context)
null))
(syntax-parse expanded
#:literals {#%expression #%plain-lambda}
[(#%expression ((~and #%plain-lambda form) (:id) :id))
(println #'form)
#'#t]
[_
#'#f])])
(try-local-expand)
The program evaluates to #t
, which means that the expanded identifier is free-identifier=?
to #%plain-lambda
, but printing it shows that it is symbolically lambda
.

Here’s a screenshot from DrRacket that illustrates what I was talking about. Notice the “Identifier binding” section in the syntax info, which says that lambda
comes from racket/private/kw.rkt
.

…and here’s the lambda
from the output. Notice that one comes from '#%core
(exported through '#%kernel
). Not the same binding! Just the same name.

I think that what you’ve said is true, and something I’d overlooked. It seems to me that (init [x 1])
really is shorthand for (init [(x x) 1])
. The docs seem pretty clear in that define-local-member-name
creates lexically scoped external names only.

So this is very helpful, but I think I still have a very basic question: since the output of local-expand
is still (lambda (x) x)
, which syntactic case in the fully expanded racket grammar does that correspond to?

My understanding was that local-expand
would fully expand the syntax you give it to fully-expanded racket using the current lexical context, but I am probably being naive in just not seeing how this fits in there.

It’s the #%plain-lambda
case. If you write (free-identifier=? #'#%plain-lambda the-lambda-from-the-expansion)
, it will return #t
. You can also print #'#%plain-lambda
in DrRacket and it will show you the same information I showed above. You can also get it programmatically by calling identifier-binding
on the identifier.

Ah!

Okay!

#%plain-lambda
is just the name #lang racket/base
exports the kernel lambda
form under (since racket/base
defines its own lambda
).

Yes, that shores up the difference and helps connect my intuition to what you’ve said here!

So, let’s say I wanted to walk over the result of local-expand
using syntax-case
. Given that the identifier could come from any number of places, what is the idiom for doing that?

Nice! This is a good example.

Well, #%plain-lambda
is the binding exported by racket/base
that is guaranteed to refer to the kernel lambda
, so you’d put #%plain-lambda
in the literal list and use that in your pattern.

Ah, okay, and then it would match. I Isee.

Right, because syntax-case will identify up to free-identifier=?
?

Yes, syntax-case
literals match by binding, in the free-identifier=?
sense.

Awesome. This is super helpful.

There’s also the syntax/kerncase
module, which exports (kernel-form-identifier-list)
, along with a kernel-syntax-case
form. But you shouldn’t be using either of those things, since you should be using syntax-parse
anyway, not syntax-case
, which provides a kernel-literals
literal set. :)


Right! We are using syntax-parse
, but I often find it’s easier to ask questions using syntax-case

The Racket documentation follows a convention, which is that whenever an identifier’s binding matters, it is typeset in blue text and is hyperlinked. Whenever an identifier is just used symbolically, it won’t be blue or hyperlinked, it will just be in plain typewriter font. Furthermore, when an identifier in the documentation is hyperlinked, you can be certain that binding is exported (under that symbolic name) from the module associated with the section that documents it (i.e. is the destination of the hyperlink).

For a good example of this convention in action, compare the grammar for cond
and the grammar for match
. In cond
, =>
and else
are blue and hyperlinked. But in match
, the identifiers in the pattern grammar (such as quote
, list
, hash-table
, etc.) are not hyperlinked.

You can see this difference for yourself. Write (define else #f)
at the top of a module, then try to use cond
with an else
clause. It won’t behave like else
. Then try to write (define list #f)
and use match
with a list
pattern. It will do the same thing it always does.

Dually, you can try (require (rename-in racket/base [else other-else]))
compared with (require (rename-in racket/base [list other-list]))
.

(A small disclaimer: this convention is only used within grammar descriptions, not within uses. The mechanism that Scribble uses to typeset codeblocks and examples does not, unfortunately, understand how to distinguish the two kinds of uses, so (match x [(quote a) #t])
will still color quote
in blue text even though its binding is irrelevant.)

Great! This is a really helpful nugget of knowledge.

Yeah, I didn’t mean to not read the documentation. I just saw that and didn’t mentally parse it since I didn’t have this in mind.

Okay, I think this all really helps. Does also give a lot more questions, too (like differences between identifier-binding
and identifier-transformer-binding
), but is helpful.

To summarize my high-level confusion in all of this is that—in my head—I was identifying “symbolic name” and “binding.”

Within the implementation of the macros themselves, what causes this difference?

I think the answer has something to do with literal sets.

I think it helps to think of the identity of a “binding” as being “the place where the thing is defined”. Even if you rename-in
something, it still refers to the same exact definition site.

So free-identifier=?
is a way of asking “are these two identifiers both references to the same definition site?”

Yes, that really helps, since my intuition comes from abstract interpretation and so I can connect well with that..

In terms of syntax-parse
features, cond
is using #:literals
(which matches by binding) and match
is using #:datum-literals
(which matches by symbolic name). In terms of lower-level concepts, cond
is comparing identifiers for equality using free-identifier=?
, while match
is comparing them for equality by getting the symbols out of the syntax object wrappers using syntax-e
and calling eq?
on the results.

Okay, that is awesome. I suspected that was the case with cond
, but I hadn’t heard of #:datum-literals

Is the reason match
doesn’t use #:literals
, just because the intention is that it is nonsensical to redefine its match patterns?

The fact that match
using symbolic comparisons instead of binding information is considered a bug, or at least a misfeature, but it’s hard to change now due to backwards compatibility concerns. The simplest answer to your question of “why?” is just “because it’s pretty old.”

Ah, okay! Yeah that’s totally fine, I didn’t know if it was something deep that indicated a design decision I should explore..

Usually, you don’t want to use #:datum-literals
. In my experience, you usually either want to use #:literals
(for when you’re either looking at fully/partially expanded syntax, or if you’re building an extensible embedded language), or you want to use keywords. It just happens that keywords didn’t always exist in Racket, so some older things that might use keywords in modern Racket use symbols instead.

Okay, that historical note really helps contextualize it.

As it happens, though, match
isn’t a place where keywords make sense, since the match
pattern language is extensible. And as soon as you need extensibility, you need to distinguish identifiers by binding, since binding is the thing that lets users add new terms to an existing language.

Right!

A good example of this in practice is syntax-parse
itself. Its pattern language uses identifiers, such as ~and
, ~do
, ~optional
, and ~parse
, and those are recognized by their binding. However, its “directive” language uses keywords, such as #:and
, #:do
, and #:with
. Its pattern language is extensible, but its directive language is not.

Yeah, that’s a great example.

I believe there’s some relatively recent record of Matthew saying something along the lines of “cond
should have used #:else
instead of else
”, since it isn’t an extensible place, but I don’t remember off the top of my head where it was from. So it’s a good rule of thumb, but note that some forms in Racket don’t follow the rule because they are old and possibly from Scheme. (Keywords are especially good in non-extensible locations because they are in a distinct syntactic category from expressions, unlike in many other languages with keywords, where keywords are “self-quoting”.)