notjack
2021-1-8 08:18:27

How do I expand a module until certain identifiers show up from an external tool? In a macro I can use local-expand with a stop-id list, but my tool isn’t locally expanding: it’s reading code from a file and turning it into a syntax object with read-syntax. Do I just have to reimplement the stop-id list feature of local-expand in terms of expand-syntax-once?


soegaard2
2021-1-8 08:20:01

Can’t you call local-expand directly?


notjack
2021-1-8 08:20:20

No, that would only work from inside the code. My tool is outside it.


notjack
2021-1-8 08:20:31

It raises the error local-expand: not currently expanding


soegaard2
2021-1-8 08:20:55

Tricky.


notjack
2021-1-8 08:24:46

Digging through the macro-debugger source I found it uses an undocumented magic parameter called current-expand-observe defined in the primitive '#%expobs module.


notjack
2021-1-8 08:25:30

I wonder what happens if I just… poke at that


soegaard2
2021-1-8 08:25:51

I think, I have seen that somewhere. Perhaps in some of Ryan’s writings?


notjack
2021-1-8 08:27:07

I’m guessing it’s some sort of hook he added to the expander to report information to the macro stepper


notjack
2021-1-8 08:29:11

Wow, cool


notjack
2021-1-8 10:23:15

So the reason I asked about the above is: I’m making a refactoring tool. And I just got it working.


sorawee
2021-1-8 10:24:48

How deep is your partial evaluator going to get?


notjack
2021-1-8 10:25:57

It fully expands the code


notjack
2021-1-8 10:26:01

Oh wait


notjack
2021-1-8 10:26:05

you mean constant folding? heh :p


notjack
2021-1-8 10:26:38

probably not very deep, I’m more interested in things like rewriting let forms to define forms


sorawee
2021-1-8 10:27:15

Ah, I see


notjack
2021-1-8 10:28:23

It’s using syntax-parse too, so rules can use syntax classes and pattern directives to match things


laurent.orseau
2021-1-8 10:34:44

That’s awesome!! Did you consider nanopass also? https://pkgs.racket-lang.org/package/nanopass


notjack
2021-1-8 10:35:50

It technically supports any function from syntax? to (option/c syntax?)


notjack
2021-1-8 10:36:18

so if nanopass can be bridged into that shape it should work


notjack
2021-1-8 10:37:06

Thank you so much @ryanc for creating current-expand-observe, hopefully I’m not doing something too awful with it: (define (expansion-events stx) (define current-expand-observe (dynamic-require ''#%expobs 'current-expand-observe)) (define events '()) (define (add-event! sig val) (set! events (cons (expansion-event sig val) events))) (parameterize ([current-expand-observe add-event!]) (expand stx)) (reverse events))


laurent.orseau
2021-1-8 10:37:18

I meant: It seems to be doing something similar to nanopass (or I misunderstand the objective), but maybe nanopass does not do what you want?


laurent.orseau
2021-1-8 10:37:47

Or if I misunderstand, mind to give a quick comparison?


sorawee
2021-1-8 10:37:52

I think the problem with nanopass is that you need a well-defined grammar to work with. But for unexpanded code, there’s no grammar (or rather, the grammar is not useful). You can’t give semantics to any piece of code at all


notjack
2021-1-8 10:37:57

It’s not doing any of the compiler stuff itself. It’s just calling racket’s read-syntax and expand functions, and listening to a bunch of messages that the macro stepper integration spits out.


laurent.orseau
2021-1-8 10:40:50

@sorawee so you mean that jack’s tool is much more flexible, because it’s case-by-case in a sense?


laurent.orseau
2021-1-8 10:41:17

@notjack but your example, and ‘refactoring’, suggest similar goals, no?


laurent.orseau
2021-1-8 10:41:30

although it could be much more general I guess


soegaard2
2021-1-8 10:43:13

@notjack Cool idea. Now we need to collect refactoring ideas for you.


notjack
2021-1-8 10:43:16

My understanding is that the problem is that nanopass doesn’t integrate with the racket macro expander. It’s not for creating embedded DSLs that use local-expand.


notjack
2021-1-8 10:43:36

@soegaard2 Throw ’em at me. Currently I’m working on a bunch of ones that rewrite let to define.


laurent.orseau
2021-1-8 10:43:58

ok, that make sense, thanks


soegaard2
2021-1-8 10:45:02

Can’t it do reindenting too? (let ([a 42] [foobarbax 32]) a-body) ==> (let ([a 42] [foobarbax 32]) a-body)


laurent.orseau
2021-1-8 10:45:24

So now I’m seeing that your tool could be used to entirely replace a racket->racket compiler, is that correct? Although it applies only before expansion, but it could well be applied as well after expansion


sorawee
2021-1-8 10:45:39

@laurent.orseau Kind of.

The way to make sense of any Racket code is to fully expand it first, so that you have a fully expanded code. But fully expanded code is too radically different from the surface syntax, so IIUC @notjack tries to find a middle ground where the code is expanded enough to reveal its meaning, but not too much to deform the program.


notjack
2021-1-8 10:50:02

Actually my program fully expands it. However, the macro expander also logs each expansion step as it expands things using that current-expand-observe function. So as the expander transforms the syntax, I’m able to add each intermediate syntax object to a list. I then filter that list for syntax-original? elements.


laurent.orseau
2021-1-8 10:50:52

Oh my, maybe I’m getting a little too excited, but it seems to be the next meta-level for Racket :star-struck:


notjack
2021-1-8 10:51:11

I think this doesn’t quite work, because syntax-original? can return true for a syntax object whose outer shape is original, but whose inner contents were inserted by a macro. I could maybe do better by checking that the syntax object is deeply original (original and every subsyntax object is original) and that the syntax-source corresponds to the file I’m refactoring.


laurent.orseau
2021-1-8 10:53:12

Why do you need to filter syntax-original elements?


laurent.orseau
2021-1-8 10:53:33

oh, because that means they are what’s written in the text file?


notjack
2021-1-8 10:53:37

right, exactly


laurent.orseau
2021-1-8 10:54:01

so if you observe a form that contains a non-original element, that means it’s too late.


notjack
2021-1-8 10:54:07

I want to refactor source code in the file, not generated code cobbled together from eighteen different macros


laurent.orseau
2021-1-8 10:54:32

I guess you should mark your own refactors as syntax-original too though?


notjack
2021-1-8 10:55:01

If you look at that screenshot of the sig = … val = … I shared earlier, look at the cases where it says sig = visit


sorawee
2021-1-8 10:55:06

e-graph is the solution to everything :slightly_smiling_face:


laurent.orseau
2021-1-8 10:56:59

what’s e-graph?


laurent.orseau
2021-1-8 10:57:01

@notjack they’re all the same. Should I pay attention to something in particular?


soegaard2
2021-1-8 10:58:15

@notjack I don’t know if this is useful or not for your purpose, but maybe? https://docs.racket-lang.org/progedit/index.html


notjack
2021-1-8 11:01:36

@laurent.orseau The current-expand-observe emits at least one visit event for every single syntax object in your program, including syntax objects created during macro expansion. I limited it to the first few there but basically the expander visited that (if …) expression three times. It also emits visit events for all of the subexpressions, as well as for a bunch of expressions that look like (#%plain-module-begin …) like you would see in the macro stepper in drracket if you turned off all macro hiding.


notjack
2021-1-8 11:01:59

@soegaard2 yup I’m gonna do something similar-ish to that


laurent.orseau
2021-1-8 11:02:32

@soegaard2 is that what raco uses or —fix-pkg-deps?


notjack
2021-1-8 11:02:42

@soegaard2 also I didn’t see your question earlier about indentation and formatting: this tool doesn’t handle that at all, it pretty much just relies on syntax->string from the syntax/to-string module.


soegaard2
2021-1-8 11:08:21

@laurent.orseau Don’t know.


soegaard2
2021-1-8 11:08:50

@notjack Ok.


laurent.orseau
2021-1-8 11:13:50

@notjack So if you’re writing something like progedit that can work with syntax-classes and syntax-parse, that’s indeed awesome :slightly_smiling_face:


notjack
2021-1-8 11:16:02

yes :grin:


notjack
2021-1-8 11:44:42

Some refactoring rules I just got (mostly) working:

(define-refactoring-rule let-to-block #:literals (let) [(let ([x:id rhs:expr] ...) body:expr ...) (block (define x rhs) ... (block body ...))]) (define-refactoring-rule single-block-elimination #:literals (block) [(block expr) expr]) (define-refactoring-rule immediate-define-block-elimination #:literals (define block) [(define header:function-header (block body:expr ...)) (define header body ...)])


notjack
2021-1-8 11:58:30

BEHOLD!


soegaard2
2021-1-8 11:59:31

Nifty!


notjack
2021-1-8 12:01:16

time for bed!


laurent.orseau
2021-1-8 12:16:20

Nice! Maybe the source-replacement could also keep track of the rule that was applied?


laurent.orseau
2021-1-8 12:22:54

That would help with debugging, one could also keep usage stats for getting rid of rules that are almost never used (imagine you have thousands of rules), or it could be used also for writing correctness proofs


laurent.orseau
2021-1-8 12:46:27

probably a good idea to track the file the replacement is applied to also.


notjack
2021-1-8 12:53:36

Yeah there’s lots of stuff like that I’m going to improve


laurent.orseau
2021-1-8 12:58:11

It should make it fairly easy to help the racket compiler with many ad-hoc rules. One could even add rules at the top of their own files if they know special cases that the compiler can’t see.


sorawee
2021-1-8 13:02:34

I thought it’s time for bed :stuck_out_tongue:


sorawee
2021-1-8 13:02:51

sorawee
2021-1-8 13:03:10

samth
2021-1-8 14:29:40

Have you seen sexp-rewrite by @ryanc?


laurent.orseau
2021-1-8 14:48:44

greg
2021-1-8 15:21:47

Sometimes I get irritated by apples/oranges name pairs.

So I go read some some point-free style code.

And I feel fortunate to have any names at all. :)


sorawee
2021-1-8 19:24:00

@spdegabrielle (or anyone who edits the wiki): is there a wiki page that lists Racket support in non English languages? There’s an open issue (https://github.com/racket/racket/issues/1902) that announces https://github.com/OnRoadZy/RackGuideInChinese. However, wiki is more suitable for this kind of information. We should add it there, and close the issue.


notjack
2021-1-8 21:27:06

Yup. I like it. Main difference between that and my thing is that my thing’s rewrite rules operate on syntax objects that have their binding structure available, so it knows not to rewrite stuff if you shadow let with your own let form. It also means rewrite rules have access to syntax-local-value, so you could make type-aware rewrites for a DSL implemented in the Type Systems as Macros style.


spdegabrielle
2021-1-8 23:35:16

@sorawee added to https://github.com/racket/racket/wiki and announced on the Racket Taiwan Discord at https://discord.gg/xpwzAcx - it accepts several languages: English, Chinese, and many languages in Taiwan!


mflatt
2021-1-8 23:37:53

I just pushed a complicated change to the readline binding (really editline) to work with CS’s requirement that callbacks are atomic.

The v8.0 release will have a much simpler change, which is the same as what was in place for a while: make the callbacks non-atomic after all.

The drawback of the simpler change is that it’s non-composable trick — it won’t cooperate with anything else that tries to use the same trick concurrently. It’s possible that using the simple trick is the right idea, though, and readline should just get special treatment. The complicated approach puts each readline call in a separate OS thread, which might have other problems, especially related to Ctl-C. So, watch out for trouble if you build from Git or use snapshots, and if there are too many problems, then the non-composable trick will probably be the way to go.


sorawee
2021-1-9 05:17:02

Thanks! :slightly_smiling_face: