greghendershott / racket-mode

Emacs major and minor modes for Racket: edit, REPL, check-syntax, debug, profile, packages, and more.
https://www.racket-mode.com/
GNU General Public License v3.0
681 stars 93 forks source link

Images not showing in repl with lindenmayer language #430

Closed GrinDeg closed 4 years ago

GrinDeg commented 4 years ago

When I try

_

lang lindenmayer

 axiom 

A

 rules 

A -> AB B -> A

 variables 

n=3

_

(https://docs.racket-lang.org/lindenmayer/A_Quick_Introduction_to_L-Systems.html)

It works and gives answer ABAAB

But when i try (https://docs.racket-lang.org/lindenmayer/Interpreting_L-systems.html)

lang lindenmayer racket

 axiom 

X  

 rules 

X -> F[+X]F[-X]+X F -> FF  

 variables 

n=7 θ=20   (require lindenmayer/turtle) (provide (all-from-out lindenmayer/turtle) X) (define (X turtles variables) turtles)

(it should draw image, and it DrRacket it does)

but it shows nothing in the REPL.

Is this normal? Can i make it show the image?

Omg, sorry for formatting

greghendershott commented 4 years ago

Thanks for letting me know. I can confirm it displays nothing for me, using the program in the link you supplied: https://docs.racket-lang.org/lindenmayer/Interpreting_L-systems.html

#lang lindenmayer racket

## axiom ##
X

## rules ##
X -> F[+X]F[-X]+X
F -> FF

## variables ##
n=7
θ=20

=============================================
(require lindenmayer/turtle)
(provide (all-from-out lindenmayer/turtle) X)
(define (X turtles variables) turtles)

Basically the image support today relies on Racket programs printing values that support file/convertible. Racket Mode's Racket back end print-handler can intercept these and create a PNG file for the Emacs Lisp front end to display.

I'll have to look at this in more depth to understand whether lindenmayer prints such file/convertible values (or not), and as a result whether this is a bug to fix or an enhancement to make, and either way what work would need to happen.

greghendershott commented 4 years ago

For now I'll assume it's a bug and tag it that way.

greghendershott commented 4 years ago

Take this little program, simple.rkt:

#lang slideshow
(circle 10)

And run it using command-line racket:

$ racket simple.rkt
#<pict>

You see #<pict> because "the #%module-begin form of racket/base wraps every top-level expression to print non-#<void> results using current-print.")) . The printed representation of (circle 10) is a pict? struct, which is opaque and therefore prints as #<pict>.

Racket Mode's back end installs a print-handler. If it sees a value that is convertible?, it tries to convert it to png-bytes, saves that to a temp .png file, and prints something like #<Image: /path/to/temp-file.png>. Then the front end Emacs code can spot such values, and replace them in the buffer with a display of the image.


If you take that Lindenmayer example program:

$ racket lindenmayer-example.rkt

It prints... nothing.

So I think the issue here is that lindenmayer/turtles programs don't evaluate to values at the module level? And/or it uses a #%module-begin that does not print?

The documentation for finish says that it returns a pict?. That would be convertible?, if Racket Mode could see it, but it doesn't. Maybe instead it assumes you're running in Dr Racket and draws directly to a dc??


That's my research so far. It's not yet clear to me if this is a bug in Racket Mode that I would be able to fix, or, a design design in lindenmayer/turtles.

greghendershott commented 4 years ago

Note: This technique of displaying images is something I originally learned from Geiser. Assuming I can fix this, in Racket Mode, I'll submit/suggest a similar fix, for Geiser.

greghendershott commented 4 years ago

OK, it turns out this has nothing to do with how the image is printed. Instead, it's about when it's printed.

lindenmayer/turtle defines a main submodule. It prints the image when the main submodule is evaluated -- not the module for the .rkt file.

Running the main submodule happens automatically in Dr Racket.

Instead, Racket Mode lets you run any submodule -- just put point inside it. (It doesn't automatically run main because maybe main has startup code, the rest of the file has support code, and you don't want main to run if you're working on the latter. You choose.)

The catch here is linenmayer/turtle defines the main submodule, not your program. So in the example program, there's no main submodule to put point in. :smile:

As a work-around: Add (module+ main (void) at the end of the example program. Due to how module+ works, this extends the main submodule; it's not an error.

#lang lindenmayer racket

## axiom ##
X

## rules ##
X -> F[+X]F[-X]+X
F -> FF

## variables ##
n=7
θ=20

=============================================
(require lindenmayer/turtle)
(provide (all-from-out lindenmayer/turtle) X)
(define (X turtles variables) turtles)

(module+ main
  (void))

Put point anywhere inside that (module+ main (void)) form, then M-x racket-run or C-c C-c. Now the image will print.


I think this answers your original question, which had a question label on it. And I'm not sure if my bug label makes sense. However I'm not going to close this, yet. I'd like to think about what if anything could/should change to make this less surprising. I'm not a fan of always running main -- maybe not even a user option to always run it. But I'll think.

florence commented 4 years ago

This solution might be an issue for languages like https://github.com/florence/pop-pl, which use the main and test submodules but don't provide a syntactic hook to them.

Maybe racket-mode could have a (user extensible) table somewhere that says "when in these languages, please always run these submodules"?

greghendershott commented 4 years ago

Maybe racket-mode could have a (user extensible) table somewhere that says "when in these languages, please always run these submodules"?

I'm OK adding such a thing as a work-around. I think the precise meaning would be, "When point is in the file module (not any submodule) instead of running that as usual, run the XXX submodule(s)".

Also, if it were an Emacs variable like racket-submodule-to-run-instead-of-file-module, you could add that to the .rkt file in a comment as a file-local variable value (or in .dir-locals.el once for all files in a dir).


Having said all that, this feels a bit weird.

I don't mean this to sound like a long rant; I'm really just typing things out in an effort to articulate why this feels weird to me. I freely admit that doesn't mean I'm right about that. :smile:

greghendershott commented 4 years ago

If you're reading my previous comment in email, I just made few minor edits that AFAIK GitHub won't email to you.

rfindler commented 4 years ago

I think you are right about the rationale for putting things into a main submodule and the lindenmayer language is hewing closer to that rationale than maybe it seems. In particular, the things in the main submodule are thigns that actually run the lindenmayer system and if you require the file you get access to the lindenmayer system as a library function.

rfindler commented 4 years ago

On a related point, I don't think that the way DrRacket chooses which submodules to run is optimal (from the UI pov). It does, by default, however run the main module when you click "Run" (as "Run" is supposed to mean "Run the program", i.e., do something roughly inspired by what racket x.rkt does and not what (require "x.rkt") does).

rfindler commented 4 years ago

(I would be happier if DrRacket and Racket Mode worked more similarly along these lines and am happy to (try to ... based on others' feedback) change DrRacket.)

greghendershott commented 4 years ago

Thanks for the feedback.

I'm also open to changing things, including giving Racket Mode users choices and guidance how to make things work more similarly to Dr Racket (or less, as they prefer).

As a tiny example, already there is a racket-run command, which keeps the edit buffer selected -- and also a racket-run-and-switch-to-repl command. I prefer the former but the latter is closer to Dr Racket -- Racket Mode even binds it to F5 by default. :smile:

I could see renaming racket-run to racket-enter-submodule-at-point. And then, a new racket-run actually means "run 'the program'" i.e. main submodule if it exists else the file module. (But I could also see making C-c C-c bound to the latter, so that the status quo doesn't change for existing Racket Mode users.)

I could also see adding a short section to Configure that discusses the various bindings and variables you can change to make Racket Mode behave more like Dr Racket.

greghendershott commented 4 years ago

Adding a "enter submodule at cursor" command to Dr Racket would be nice, IMHO. I find it extremely handy in Racket Mode. Run main, tests, slow-tests, example, or whatever submodules make sense.

It might be harder for Dr Racket to implement for unsaved editors. (Racket Mode auto-saves the file, first.) Maybe just... name the command "save file and enter submodule at cursor". :smile: I'm only partly joking.

rfindler commented 4 years ago

I would like to see the "run the program and be inside the module at the point" functionality in DrRacket. How does racket mode achieve that? Does it explicitly expand the program and look for submodules as part of the process of running a program? (DrRacket doesn't explicitly get hold of the fully expanded program during running).

greghendershott commented 4 years ago

It just looks for module/module*/module+ forms in the source, textually, walking up from point (cursor), reverses the list, and gives the module path to module->namespace.

Yes, textually. OTOH as an alternative to supplying such text in some UI, and as (> something nothing), I think it's been fine so far. Admittedly does not work in non-sexpr langs or those that rename module.

greghendershott commented 4 years ago

(And so, if drracket/check-syntax were to return annotations about modules, that would be a better way for this to work, someday.)

greghendershott commented 4 years ago

@GrinDeg If you're feeling spammed by this issue discussion being so long, for your simple question, I apologize. Remember you can click its Unsubscribe button in the GitHub web site UI! :smile:

rfindler commented 4 years ago

re "It just looks for module/module*/module+ forms in the source, textually ...": oh. I'm not excited to add that to DrRacket. As for check syntax reporting something about a mapping from source locations to submodules, that is what I'd considered before but the problem is that it won't work until the little bubble turns green and I fear that would lead to confusion for the user. Maybe there could be two keystrokes: one that just runs a fixed set of modules and one that runs the ones at th epoint and the second keystroke could somehow indicate it wasn't ready to run somehow if the information about that mapping wasn't present?

GrinDeg commented 4 years ago

@GrinDeg If you're feeling spammed by this issue discussion being so long, for your simple question, I apologize. Remember you can click its Unsubscribe button in the GitHub web site UI! smile

@greghendershott Thank you so much! It's ok :D

greghendershott commented 4 years ago

re "It just looks for module/module*/module+ forms in the source, textually ...": oh. I'm not excited to add that to DrRacket

OK. :man_shrugging:

To be clear, by "textually" I mean it walks the Emacs "syntax-propertized" buffer structure --- possibly similar to a color-lexer. So it is not finding rando things like "level" in a comment ;; find all (module level) bindings.

But it won't work e.g. on #langs that let you define submodules but don't use sexprs. Someday maybe a Racket Mode user will point out any such example that matters to them, and maybe I'll try to deal with that then. Meanwhile, the feature seems useful to at least a few Racket Mode users.

Maybe this is a case where the perfect-forever might be the enemy of the good-for-the-forseeable-future. Of course that's my opinion based on my priorities.

greghendershott commented 4 years ago

As for check syntax reporting something about a mapping from source locations to submodules, that is what I'd considered before but the problem is that it won't work until the little bubble turns green and I fear that would lead to confusion for the user. Maybe there could be two keystrokes: one that just runs a fixed set of modules and one that runs the ones at th epoint and the second keystroke could somehow indicate it wasn't ready to run somehow if the information about that mapping wasn't present?

That could work. Also assuming the old annotations from a previous check-syntax are not immediately invalidated by a user editing, and assuming they're "sticky" and move with text after insertion or deletion (as with emacs text-properties): It is highly likely they will remain useful after the initial background check. So there might be an initial disable state, but maybe only until the first check-syntax finishes. That wouldn't be too bad.

greghendershott commented 4 years ago

@GrinDeg I pushed commit f80d48c for this. However it is on a check-syntax branch that I've been working on nearly full-time for the last two months. It was cleaner for me to do it that way, because it's in some code that was changing and getting cleaned up, already. I hope to merge to master, before too much longer. Anyway, this issue will auto-close once it is merged to master, but if you have any questions feel free to let me know even after that happens.

rfindler commented 4 years ago

re "It just looks for module/module*/module+ forms in the source, textually ...": oh. I'm not excited to add that to DrRacket

OK. 🤷‍♂

To be clear, by "textually" I mean it walks the Emacs "syntax-propertized" buffer structure --- possibly similar to a color-lexer. So it is not finding rando things like "level" in a comment ;; find all (module level) bindings.

But it won't work e.g. on #langs that let you define submodules but don't use sexprs.

Someday maybe a Racket Mode user will point out any such example that matters to them, and maybe I'll try to deal with that then. Meanwhile, the feature seems useful to at least a few Racket Mode users.

I agree it is useful! No doubt. That's not the issue. But since we're talking about it, I guess it does not work on things like (lambda (module) (module 'test))?

I'm trying to move drracket in a direction where it is language agnostic which means finding solutions to problems like these in a language-agnostic way, not giving up on the problem.

Maybe this is a case where the perfect-forever might be the enemy of the good-for-the-forseeable-future. Of course that's my opinion based on my priorities.

Sure. Absolutely. No blame from me! :)

rfindler commented 4 years ago

As for check syntax reporting something about a mapping from source locations to submodules, that is what I'd considered before but the problem is that it won't work until the little bubble turns green and I fear that would lead to confusion for the user. Maybe there could be two keystrokes: one that just runs a fixed set of modules and one that runs the ones at th epoint and the second keystroke could somehow indicate it wasn't ready to run somehow if the information about that mapping wasn't present?

That could work. Also assuming the old annotations from a previous check-syntax are not immediately invalidated by a user editing, and assuming they're "sticky" and move with text after insertion or deletion (as with emacs text-properties): It is highly likely they will remain useful after the initial background check. So there might be an initial disable state, but maybe only until the first check-syntax finishes. That wouldn't be too bad.

Totally!

greghendershott commented 4 years ago

Closed via commit 5226366.