fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Hot Reload

Open Happypig375 opened this issue 3 years ago • 46 comments

Is your feature request related to a problem? Please describe.

C# is getting Hot Reload for .NET 6. Will F# receive the same experience? Roslyn has had work done to support this. The F# compiler doesn't seem to have any equivalent work on this.

Describe the solution you'd like

F# can use hot reload just like C#.

Describe alternatives you've considered

Staying behind in terms of tooling again.

Additional context

If the issue is about:

  • improving a compiler error message: No.
  • improving/adjusting the parser: No.

Add any other context or screenshots about the feature request here. https://github.com/dotnet/core/issues/5510

Happypig375 avatar Jun 05 '21 09:06 Happypig375

https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/

F# is currently not supported in .NET 6 but we are planning to support in a future release based on customer feedback.

Of course F# is staying behind in terms of tooling again. 😞

Happypig375 avatar Jun 05 '21 09:06 Happypig375

What's the current status if:

  • You run a C# project that supports edit and continue/hot reload
  • This depends on an F# project
  • After making a change in the F# project, you build it.

Is hot reload able to support that currently? I.e. loading the updated assembly.

charlesroddie avatar Jun 05 '21 10:06 charlesroddie

It'll happen eventually here, but hot reload is a very new technology that is likely to undergo several changes well after .NET 6. We'll plug into it when it's more stable.

cartermp avatar Jun 05 '21 16:06 cartermp

@happypig, @charlesroddie -- what do you want hot reload to do for you? Just asking for a feature name is probably not going to get anything moving. We don't have plenty of other C# features also, and we don't plan on going down the VB route of moaning whenever we don't get some new C# feature.

KevinRansom avatar Jun 08 '21 21:06 KevinRansom

For me personally the biggest win is with projects that we start with "dotnet watch run". Usually those are expecto tests or asp.net core servers. If dotnet watch run would work like fable and keep a compiler in memory that only recompiles the stuff that is dependent on the files that I touched, then the dev cycle could speed up dramatically. I don't think we would need hot module replacement like webpack does - just restart the process like dotnet watch run would do right now.

Kevin Ransom (msft) @.***> schrieb am Di., 8. Juni 2021, 23:46:

@happypig https://github.com/happypig, @charlesroddie https://github.com/charlesroddie -- what do you want hot reload to do for you? Just asking for a feature name is probably not going to get anything moving. We don't have plenty of other C# features also, and we don't plan on going down the VB route of moaning whenever we don't get some new C# feature.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dotnet/fsharp/issues/11636#issuecomment-857186796, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAOANETHSOIUAQRKRF6SVLTR2FSPANCNFSM46EKO77A .

forki avatar Jun 09 '21 05:06 forki

From the linked thread:

https://github.com/dotnet/core/issues/5510#issue-734882804 How fast developers can make code changes and see the resulting impact in their apps is directly proportional to how productive they can be.

Most important is a quick development loop when changing bits of UI (likely to be MAUI with WinUI as primary development platform). For non-UI code I don't need to run code very often, sometimes only once after finishing a work item to check that it works. But for UI code a lot of tweaks and adjustments are important and need to be visualized by running the code.

Hot reload would be perfect for this but does look like a very large task for F# to support Edit and Continue which it is based on.

A typical UI tweak doesn't affect the API of an assembly so reference assemblies will speed up the loop.

Hypothetically if it were possible, while running an application, to edit a dependent project, build it, and switch in the dll, it might capture most of the benefit of hot restart. But this may not be possible in dotnet?

charlesroddie avatar Jun 09 '21 07:06 charlesroddie

Would support for generating files for FSI scripting, like the one provided by the F# Power Pack back in the day, be an alternative in this case? Together with script debugging, of course. I can do it in VS 2015 with the plugin for something like winforms, for example. It's more clunky than have it done automatically, for sure, but also closer to home.

heronbpv avatar Jun 09 '21 22:06 heronbpv

I am using blazor with fsharp. Hot reload will speed up the poductivity a lot.

albertwoo avatar Jun 24 '21 01:06 albertwoo

This is a critical tooling enhancement for my usage of F#.

Is there some way I can contribute to move up the timeline? (I don't currently have a great feel for the scope of work needed)

farlee2121 avatar Jul 20 '21 15:07 farlee2121

This is a critical tooling enhancement for my usage of F#.

Is there some way I can contribute to move up the timeline? (I don't currently have a great feel for the scope of work needed)

I don't think we have a full understanding of what has to be done yet.

vzarytovskii avatar Jul 20 '21 15:07 vzarytovskii

I decided to spelunk and get a better idea of what it takes to implement hot reload.

Full notes are in this gist https://gist.github.com/farlee2121/24915fb0518ad4ca6631bc33d0ff401c

Finds so far

  • Dotnet CLI implementation looks like it'll also cover Visual Studio support
    • source: https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/
  • Need to add a hot reload project capability to f# projects
    • example: https://github.com/dotnet/sdk/issues/19608
  • The change watching is done at the file level before language-specific considerations
    • source: https://github.com/dotnet/sdk/blob/baecd78dd60fc32f2cc942d3df6a520c28ac96ad/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs#L131
  • Delta appliers appear to be be reusable
    • this should get us desktop, asp.net core, and web assembly support out of the box
    • here's one of the DeltaAppliers
  • Diffs generators will need to be implemented for F#
    • The diff shape should be as defined by Microsoft.CodeAnalysis.ExternalAccess.Watch.Api.WatchHotReloadService.Update in order to reuse DeltaAppliers
    • We'll need to compute scope of recompilation and generate prartial updates for IL, pdbs, and metadata
  • Hot reload depends on hard-coded services and file extensions
    • probably add our implementation to Microsoft.DotNet.Watcher.Tools.HotReload.cs because CompilationHandler is tied to Roslyn
    • WARN: This prevents modular deployment, and we may not want to ship the F# implementation to all users
  • README for hot reload

Oustanding investigations

  • TODO: Looking into F# partial compilation capabilities
  • TODO: Look into support for solutions with projects in multiple languages
  • TODO: understand where application state is preserved

farlee2121 avatar Aug 23 '21 23:08 farlee2121

A quick update. I made an experimental fork.

It's pretty basic so far

  • Handles project changes
  • Handles new files
    • There is a gotcha with VisualStudio. The build fails because the VS implicitly modifies the project file, but doesn't save it right away
  • Rebuilds on any change to any .fs file

The running program will show changes, and much faster than the existing dotnet watch. ~~The main issue is that debug artifacts are not updated with this strategy.~~

A more accurate description is that rebuilding tries to output both dlls and pdbs, but those resources are already in use by the debugger process.

farlee2121 avatar Sep 01 '21 15:09 farlee2121

@farlee2121 Don't be shy, post a GIF!

NatElkins avatar Sep 02 '21 14:09 NatElkins

Here's a demonstration of the most basic scenario, changing a file

fsharp-watch

farlee2121 avatar Sep 06 '21 13:09 farlee2121

Sorry for my ignorance, but what exactly is that demonstrating? Watch will recompile and relaunch an application today without hot reload.

kerams avatar Sep 06 '21 14:09 kerams

Correct.

Here is the described state of my experiment I was asked to gif.

It's pretty basic so far

  • Handles project changes
  • Handles new files
    • There is a gotcha with VisualStudio. The build fails because the VS implicitly modifies the project file, but doesn't save it right away
  • Rebuilds on any change to any .fs file

I also believe it to be faster, but my sample is only one project.

While the above requirements are met with the DotNetWatcher, they also have to be implemented for F# in HotReloadDotNetWatcher.
You could interpret this gif as demonstrating the most basic F# reloading, but working in the Hot Reload implementation of dotnet watch.

I'm still working on demonstrating updates to F# code using existing delta appliers (true hot reload).

farlee2121 avatar Sep 06 '21 16:09 farlee2121

I see, thank you.

kerams avatar Sep 06 '21 16:09 kerams

An update on my efforts.

  • I started some new consulting projects, so my time for this has declined and progress has slowed.
  • I've had to back-fill a lot of knowledge about compiled artifacts (dlls, pdbs)
  • I thought I had figured out the data format needed for live updates applied by IDeltaApplier by digging through the roslyn implementation. However, my test implementation didn't work.

A few other conclusions likely obvious to the FSharp team, but I had to dig for

  • It appears FSharp Compiler Services has the underlying tooling we need to generate incremental updates
  • Incremental updates can be created using FSharpChecker then diffing the compiled outputs, but it seems we could make smarter updates with access to internal-only API methods. Thus, we'll likely want to add hot reload binary patch /delta generation to the compiler services and simply invoke it from the appropriate place in dotnet-watch

Things I'll probably need help on if I can demonstrate a successful reload

  • Project cracking is very confusing, I traced a lineage of several deprecated project crackers, we probably don't want core tooling to depend on the current ionide cracker. How are we supposed to build a project graph?
  • Feedback on where the hot reload delta generation should live in compiler services
  • Understanding live update limitations (rude edits)

farlee2121 avatar Sep 23 '21 18:09 farlee2121

I suppose @farlee2121 needs someone from Roslyn/HotReload team to join this thread

xperiandri avatar Sep 24 '21 09:09 xperiandri

That would certainly help

farlee2121 avatar Sep 24 '21 17:09 farlee2121

I got a bit more work in tonight. Specifically, I traced how the tests were generating deltas, and it comes back to a utility based on the same Roslyn method I was looking at before . The issue is that it passes around a bunch of mutable byte streams and I'm having a hard time tracking with what all happens to those mutable streams.

I also set up more experiments to understand what behaviors hot reload handles for C# now. I found the results more fickle than I expected. Asp.net works fine, but I've yet to get blazor wasm working, and the console app only updates if I stop at a breakpoint.

Issue Scope

My updates have been a bit hodge-podge, so here's a summary in light of the original question: what would it take to implement hot reload for F#.

I'm more confident than ever that the DeltaAppliers should be reusable for F#. "Starting in 6.0, the runtime (.NET Core and Mono) expose APIs to patch a running app". This should be language-agnostic, and addresses the complexities of different runtime conditions.

My sdk fork demonstrates how to plug F# into the hot reload hierarchy. I can provide more details if needed. It's basically the single FSharpCompilationHandler class.

The main work to be done is an F# compiler api method that takes changed files in a project and outputs assembly patches in a format mappable to System.Reflection.Metadata.MetadataUpdater. We still need to understand that format properly. This api method should also notify when a "rude edit" that can't be handled at runtime is made so hot reload can prompt for a restart.

When it's all done, we add <ProjectCapability Include="SupportsHotReload" /> to the Fsharp targets file

farlee2121 avatar Oct 09 '21 02:10 farlee2121

Any progress so far?

albertwoo avatar Dec 10 '21 03:12 albertwoo

I started a new job and haven't had time for this lately. I'll probably need to reach out to the roslyn team to better understand the delta formats

farlee2121 avatar Dec 18 '21 03:12 farlee2121

@dsyme can you please help on this cool feature? Do you know any Roslyn team members who can help here?

albertwoo avatar Dec 23 '21 03:12 albertwoo

F# support for this would be so much appreciated. I created a small repository to illustrate the problem for the new readers: https://github.com/PiotrJustyna/binoculars What I'm after is for the PID not to change as the code hot-reloads. It does work perfectly for C# 👌

I plan to use it for work where, in order to speed up deployments, we could hot swap certain F# components in our C# and F# hosts. Eagerly observing this issue 👀

PiotrJustyna avatar Jan 20 '22 10:01 PiotrJustyna

This feature would improve our team daily workflow. I don't understand why is not being planned to introduce for F# language. It will leave the language as a second class citizen when compared with C# and even VB.NET.

askpt avatar Jan 21 '22 09:01 askpt

It can improve bolero developments. I am also making a library Fun.Blazor, looking forward to hot reload for a long time. I hope F# team can take this as soon as possible, they and Roslyn team are all in Microsoft right? They can discuss the C# implementation detail. Hope it will happen soon...

albertwoo avatar Jan 22 '22 01:01 albertwoo

The lack of hot reload is also holding me back from using Bolero with F# instead of Blazor with C#.

AlexBoehm avatar Feb 02 '22 11:02 AlexBoehm

This missing feature is really a productive killer and will not help to spread the usage of F#. If you gain initial benefits from using F# and then later your productivity falls back behind using C# what is the rationale to use F# outside of domains like Data Science ?

Even if you have F# type references in your C# assembly, hot reload seems not to work even for C# most of the time. This is even more frustrating.

I understand that rolling out new features cannot be done always in a holistic way through the .NET framework. But if evertyime of such a feature, F# user has to wait a couple of years until they might be able to benefit also, F# will never get more momentum, the IDEs will always be considerable less productive for F# devs in general. So if F# should be a first citizen in .NET please think about improving this situation in the future.

coldwarrl avatar Feb 15 '22 05:02 coldwarrl

F# can be transpiled to JavaScript (now also Python) via Fable and thus it uses hot reload feature from JavaScript. If F# could also be transpiled to C# via Fable, does it mean that F# would get C#'s hot reload feature for free?

badgh avatar Mar 16 '22 13:03 badgh