paul
2018-10-23 14:53:16

Yes, I read that post. I says that I have to build the entire system, or at least the minimal system. I was wondering if there is a shortcut.


samth
2018-10-23 14:54:51

I recommend following what’s in that post


paul
2018-10-23 16:06:27

Okay.


soegaard2
2018-10-23 21:39:34

Hi All. I have a run into a problem with a macro. I have a hunch that I am missing something simple. In short I want references to undefined variables to evaluate to false. This macro is defined in the file “top.rkt”. The macro seems to work, but when I use it in the context I need, it doesn’t.


soegaard2
2018-10-23 21:39:39

The example is here:


soegaard2
2018-10-23 21:39:41

lexi.lambda
2018-10-23 21:48:08

@soegaard2 To be clear, you’re trying to evaluate the descriptor as an expression at phase 1, and you want an unbound reference to produce #f?


lexi.lambda
2018-10-23 21:49:41

Normally the way to do something like that is to, instead of storing the static information in a phase 1 definition, store it in a phase 0 define-syntax definition, then look it up later with syntax-local-value. Is there a reason you can’t do that here?


soegaard2
2018-10-23 21:51:42

What’s the difference between a phase 1 definition and a phase 0 define-syntax ?


lexi.lambda
2018-10-23 21:54:16

Every definition has two parts: the bound identifier and the expression on the RHS. An ordinary define binds its identifier in the current phase to an expression evaluated in that same phase. This is true whatever phase the define lives in: both the binding and the expression are just shifted to some other phase. But define-syntax is special: it binds an identifier in the current phase to an expression evaluated in the current phase + 1.


lexi.lambda
2018-10-23 21:54:56

Put another way, define-syntax implements “glue” between Racket’s different phases: it’s what connects the binding environment of phase 0 to the value environment at phase 1.


lexi.lambda
2018-10-23 21:57:16

It’s generally advantageous to put your bindings in phase 0 because they will behave in ways that users generally expect: if they declare a new record in a local scope, things will just work so long as all the bindings are defined at phase 0 (even if an outer binding is shadowed). The same is not true if you try to create a binding at phase 1, since this program is illegal: (let () (begin-for-syntax ; not allowed here! (define x 1)) (void))


soegaard2
2018-10-23 22:00:02

I think you might be right, that define-syntax and syntax-local-value is the way to go. But nevertheless, here is the thought process I followed:


soegaard2
2018-10-23 22:01:00

Let’s say we have defined a sprite record: (define-record sprite (x y))


soegaard2
2018-10-23 22:01:32

Then we create an instance: (define ball (sprite 11 12))


soegaard2
2018-10-23 22:02:28

ball now lives at phase 0, but when ball.x is to be transformed into (sprite-x ball) that will happen at phase 1


soegaard2
2018-10-23 22:02:48

thus the type of ball should be stored at phase 1


soegaard2
2018-10-23 22:03:04

Hence an ball:type defined at phase 1 makes sense.


lexi.lambda
2018-10-23 22:09:29

That does make sense, intuitively. :)


lexi.lambda
2018-10-23 22:10:31

But I think one way to think about this is that Racket has a very rich language for manipulating bindings: you can rename them, add scopes to them, remove scopes from them, forge them out of thin air, and all sorts of other silly things.


lexi.lambda
2018-10-23 22:11:31

So if you want to ensure that your phase 1 information is correlated with the phase 0 binding that the user thinks about, you need to attach the information to that binding, not to some other binding that you can correlate by hoping they’re both in scope at the same time with different names (and in this case, at different phases).


lexi.lambda
2018-10-23 22:12:58

You can do this in different ways—you can use define-syntax and syntax-local-value, as I mentioned, but you could also use a free-id-table to stash the value—but the key is that the extra information is stored with the binding.


lexi.lambda
2018-10-23 22:14:20

As for why your particular example doesn’t work, for what it’s worth, the problem is likely because (let-syntax ([#%top top]) ....) binds #%top inside the macro, but not in the scope of var:type.


soegaard2
2018-10-23 22:21:23

One thing I dislike about the syntax-local-value approach is that I can’t write (begin-for-syntax (displayln ball:type)) when debugging.


lexi.lambda
2018-10-23 22:21:59

You could do (begin-for-syntax (displayln (syntax-local-value #'ball))) instead.


soegaard2
2018-10-23 22:23:31

That doesn’t work since syntax-local-value only works during expansion - so I need to write (let-syntax ([foo (λ (stx) (displayln (syntax-local-value #’ball:type)))]) foo)


soegaard2
2018-10-23 22:24:07

Hmm. I suppose I could wrap that into a macro.


lexi.lambda
2018-10-23 22:24:12

It works inside begin-for-syntax as well as while a macro is being expanded.


soegaard2
2018-10-23 22:24:58

Oh - it does! I could have sworn I already tried that.


lexi.lambda
2018-10-23 22:25:07

The documentation says this: > This procedure must be called during the dynamic extent of a syntax transformer application by the expander or while a module is visited (see syntax-transforming?), otherwise the exn:fail:contract exception is raised. “while a module is visited” is when begin-for-syntax blocks are evaluated and when the RHSs of top-level define-syntaxes are evaluated.


soegaard2
2018-10-23 22:26:55

The error reported from syntax-local-value is worded as “syntax-local-value: not currently expanding”.


lexi.lambda
2018-10-23 22:28:15

Technically, the error is not wrong, as modules are visited during expansion.


lexi.lambda
2018-10-23 22:28:26

(Though it is maybe less clear than it could be.)


soegaard2
2018-10-23 22:34:13

I made the switch to syntax-local-value and it does simplify the code. Thanks.