spdegabrielle
2020-9-7 16:28:32

Someone told me recently that that hotloading is impossible in Racket, but I feel like I do the every time I use DrRacket, or change a script in Quickscript? What is the distinction I’m missing here? (I’m not restarting DrRacket every time I hit run?)


spdegabrielle
2020-9-7 16:31:03

And if I can do that in DrRacket, why not in the webserver?


laurent.orseau
2020-9-7 16:31:05

There’s no good basis for this claim. dynamic-require is all you need for hotloading (unless my definition doesn’t match theirs)




spdegabrielle
2020-9-7 17:34:53

Thank you @soegaard2


samth
2020-9-7 17:38:05

You need to create new namespaces too, otherwise the second dynamic-require doesn’t do anything (I know you know that, @laurent.orseau)


samth
2020-9-7 17:39:10

Hot loading requires somewhat more work in Racket than in Closure or Common Lisp


spdegabrielle
2020-9-7 17:40:54

Is that a immutability issue? - in the sense that it makes for safer code?


spdegabrielle
2020-9-7 17:42:19

Also usability like the repl


samth
2020-9-7 17:44:10

It’s mostly that there’s no way to change the definition of a module after it’s loaded


spdegabrielle
2020-9-7 17:44:56

Sounds like a good thing?


samth
2020-9-7 17:46:13

In Clojure or CL, all variable references to top level variables are in directed though effectively a big mutable table


samth
2020-9-7 17:48:07

And redefinition just mutates that table


samth
2020-9-7 17:48:20

So redefinition affects earlier definition


samth
2020-9-7 17:48:27

Just like in the Racket repl


samth
2020-9-7 17:49:14

If you do everything with the repl and load in Racket then it’s just as easy to do hot reloading


spdegabrielle
2020-9-7 17:50:30

Lexical scope doesn’t apply?


samth
2020-9-7 17:52:33

For lexical variables, yes, but for top level definitions it’s not clear what that means


spdegabrielle
2020-9-7 17:53:27

Eurghh!


spdegabrielle
2020-9-7 17:53:47

I’d always assumed order counted


samth
2020-9-7 17:55:08

Then you couldn’t define mutually recursive functions at the repl


samth
2020-9-7 17:55:25

What you suggest is how the ML repl typically works


spdegabrielle
2020-9-7 18:14:08

Ahh


spdegabrielle
2020-9-7 18:14:23

So much to learn.


spdegabrielle
2020-9-7 20:01:48

@samth can you suggest a resource I can use to learn more about this ? ( is there a text?)


samth
2020-9-7 20:06:55

No, there’s no text I know about for repls


spdegabrielle
2020-9-7 20:08:49

Thanks anyway.


samdphillips
2020-9-7 20:14:18

What I mean is that within a class form (from racket/class) a function application with a method is transformed to (send this method args ...) similarly for variable references for fields.


spdegabrielle
2020-9-7 20:14:35

I’m assuming dynamic-require+new namespace is what is going on when you hit run in DrRacket or load a new quickscript without restarting DrRacket each time ? (in the same way https://github.com/tonyg/racket-reloadable\|https://github.com/tonyg/racket-reloadable uses dynamic-require)


samth
2020-9-7 20:15:25

DrRacket more directly calls eval because it isn’t running a file on disk, but yes


spdegabrielle
2020-9-7 20:16:37

Thank you:grinning:


laurent.orseau
2020-9-7 20:27:18

That’s what quickscript uses indeed. MrEd Designer also uses dynamic-require to load the plugins, but it doesn’t use namespaces since the plugins don’t need to be reloaded. (this allows for designing new widget classes without touching the core of mreddesigner)


spdegabrielle
2020-9-7 20:39:48

I forgot about MrEd Designer!


rokitna
2020-9-8 00:28:21

“Hyper-static global environments” come up in this kind of conversation. So do fexprs.

Both approaches lead to better consistency between functions and macros: Fexprs make them both reloadable, and hyper-static global environments make neither of them reloadable. (I’ve also heard of people making both reloadable without resorting to fexprs, by keeping track of macro uses and re-expanding things when the macro is redefined.)


samth
2020-9-8 00:32:44

I’m not sure what hyper-static global environments are, but fexprs are really not related to reloading (and a bad idea besides)


rokitna
2020-9-8 01:08:03

While I probably agree at the “bad idea besides” level, I don’t see how they could be unrelated. With compile-time macroexpansion, existing macro calls “still use” the old definition because they already used it in a compile-time step that was taken care of before the redefinition even took place. An easy-to-propose way to “fix” this is to make macro calls as lazy as function calls: Have them wait until just before a call is evaluated (at run time) to look up the macro’s definition, so that its latest redefinition can be taken into account. That’s more or less what fexpr calls do.


samth
2020-9-8 01:13:32

but this just restates the issue for functions. the central problem of the top level is what do references mean — do the refer to the thing defined at the time of definition, or do they “see” redefinitions. the question is “do we want everything to be indirected through a big mutable table so that it can be changed at any time”, and whether or not to have fexprs is orthogonal.


rokitna
2020-9-8 05:35:32

Fexprs are one popular way to make macros redefinable (inasmuch as it’s at all popular for macros to be redefinable), and mutable environments are one popular way to make functions redefinable. There are other ways that I think are preferable to either one of these, such as listening for redefinitions and triggering recompilation of things that call them.

It seems to me the topic at hand is hot reloading of code. Depending on what kind of code someone wants to reload, it could contain both function and macro definitions. But I’ll set the macro side of this aside now, so that I’m not continuing to defend fexprs. :-p

At a basic level, if the user intends to redefine something, then they’re contradicting the user intent they expressed before. The question “what do references mean” doesn’t have any simple answer in this situation; whatever the user intended when they wrote those references in the first place is no longer what they want them to mean now.

And yet this kind of change in user intent can never be dismissed as “hopeless” because it’s often pretty normal and healthy to change one’s mind. It’s nice to have tooling features that accommodate short-term user mistakes (e.g. undo), help them visualize the choices they’re making, help them migrate from old choices to new ones (e.g. database migrations and API versioning), and build up their long-term ability to build custom tooling in anticipation of their own future intent-expressing needs.

When I imagine a user experiencing things like undo support, guided choice-making, and data migration in the process of redefining something in a REPL, I’m not sure that “do we want a mutable table” is the full extent of the question.

Still, since when does a REPL have all those niceties? Can I be sure the user actually wants a data migration tool in the middle of their REPL section? It could easily be premature abstraction. Because of that, I don’t think I can hold an opinion very strongly here; there’s just a design space, and I’m more hopeful about some points in the space than others.

In the meantime, I’m not too familiar with the ML REPL, but I think a hyper-static global environment and what the ML REPL does are the same thing. It’s just a name I wanted to put out there to connect it to other discussions that exist on the same topic.