soegaard2
2019-4-3 09:24:19

@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.


lexi.lambda
2019-4-3 14:35:37

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


philip.mcgrath
2019-4-3 15:03:18

@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.


lexi.lambda
2019-4-3 15:40:33

@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.


lexi.lambda
2019-4-3 15:41:45

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.


lexi.lambda
2019-4-3 15:42:00

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


lexi.lambda
2019-4-3 15:44:07

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.


lexi.lambda
2019-4-3 15:46:12

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.


lexi.lambda
2019-4-3 16:07:37

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?


leif
2019-4-3 16:22:41

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


leif
2019-4-3 16:22:56

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



leif
2019-4-3 16:24:59

(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?)


mflatt
2019-4-3 16:50:04

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.


lexi.lambda
2019-4-3 16:52:09

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.)


mflatt
2019-4-3 16:57:57

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


lexi.lambda
2019-4-3 16:59:57

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?


soegaard2
2019-4-3 17:46:17

philip.mcgrath
2019-4-3 18:28:46

@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.


philip.mcgrath
2019-4-3 18:36:37

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:


philip.mcgrath
2019-4-3 18:37:33

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


philip.mcgrath
2019-4-3 18:38:09

“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 ….”


philip.mcgrath
2019-4-3 18:41:08

“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.”


philip.mcgrath
2019-4-3 18:41:44

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


philip.mcgrath
2019-4-3 18:43:19

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


philip.mcgrath
2019-4-3 18:53:47

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.


philip.mcgrath
2019-4-3 19:02:31

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).


githree
2019-4-3 19:20:40

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


pierre
2019-4-3 20:00:03

: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…


philip.mcgrath
2019-4-3 20:04:18

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


pierre
2019-4-3 20:05:28

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


pierre
2019-4-3 20:05:38

but this does not work as expected


philip.mcgrath
2019-4-3 20:10:30

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.


pierre
2019-4-3 20:12:18

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


alexknauth
2019-4-3 20:13:50

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


philip.mcgrath
2019-4-3 20:14:46

Here’s a first attempt with #:struct:


philip.mcgrath
2019-4-3 20:20:33
(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 : (-&gt; 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).


pierre
2019-4-3 20:22:18

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


pierre
2019-4-3 20:23:21

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


pierre
2019-4-3 20:24:21

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…


mflatt
2019-4-3 21:38:40

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


matias
2019-4-3 21:44:21

That would explain the weird error messages I was getting…


lexi.lambda
2019-4-3 21:46:03

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


ben
2019-4-3 22:25:01

@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)


krismicinski
2019-4-4 01:00:54

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.


lexi.lambda
2019-4-4 01:56:43

@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.


lexi.lambda
2019-4-4 01:57:29

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.


philip.mcgrath
2019-4-4 02:00:00

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.


lexi.lambda
2019-4-4 02:04:01

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.


lexi.lambda
2019-4-4 02:05:06

…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.


lexi.lambda
2019-4-4 02:13:12

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.


krismicinski
2019-4-4 02:19:01

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?


krismicinski
2019-4-4 02:20:23

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.


lexi.lambda
2019-4-4 02:20:42

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.


krismicinski
2019-4-4 02:20:54

Ah!


krismicinski
2019-4-4 02:20:56

Okay!


lexi.lambda
2019-4-4 02:21:13

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


krismicinski
2019-4-4 02:21:22

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


krismicinski
2019-4-4 02:22:01

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?


krismicinski
2019-4-4 02:22:36

Nice! This is a good example.


lexi.lambda
2019-4-4 02:22:38

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.


krismicinski
2019-4-4 02:22:53

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


krismicinski
2019-4-4 02:23:07

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


lexi.lambda
2019-4-4 02:23:14

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


krismicinski
2019-4-4 02:23:24

Awesome. This is super helpful.


lexi.lambda
2019-4-4 02:24:05

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. :)



krismicinski
2019-4-4 02:24:34

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


lexi.lambda
2019-4-4 02:27:22

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).


lexi.lambda
2019-4-4 02:29:32

For a good example of this convention in action, compare the grammar for cond and the grammar for match. In cond, =&gt; 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.


lexi.lambda
2019-4-4 02:30:41

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.


lexi.lambda
2019-4-4 02:31:20

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


lexi.lambda
2019-4-4 02:33:58

(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.)


krismicinski
2019-4-4 02:36:21

Great! This is a really helpful nugget of knowledge.


krismicinski
2019-4-4 02:44:15

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.


krismicinski
2019-4-4 03:04:43

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.


krismicinski
2019-4-4 03:07:31

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


krismicinski
2019-4-4 03:12:27

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


krismicinski
2019-4-4 03:14:15

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


lexi.lambda
2019-4-4 03:14:25

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.


lexi.lambda
2019-4-4 03:15:01

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


krismicinski
2019-4-4 03:16:48

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


lexi.lambda
2019-4-4 03:17:22

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.


krismicinski
2019-4-4 03:18:24

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


krismicinski
2019-4-4 03:20:06

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


lexi.lambda
2019-4-4 03:21:19

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.”


krismicinski
2019-4-4 03:21:41

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


lexi.lambda
2019-4-4 03:21:55

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.


krismicinski
2019-4-4 03:22:20

Okay, that historical note really helps contextualize it.


lexi.lambda
2019-4-4 03:22:59

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.


krismicinski
2019-4-4 03:24:55

Right!


lexi.lambda
2019-4-4 03:25:08

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.


krismicinski
2019-4-4 03:27:38

Yeah, that’s a great example.


lexi.lambda
2019-4-4 03:27:44

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”.)