-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Source Generators #864
Comments
You can also write an F# script to generate an F# file today. |
A huge part of type providers is design-time support. Based on a quick look, source generators don't seem to be anything like that; more akin to/just an evolution of T4. I'd personally rather see https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1023-type-providers-generate-types-from-types.md than this. |
@kerams everybody seems to agree that type provider are enormously brittle (and that seems to be an understatement still) so having a standard, integrated, lightweight source generation facility would be more than welcome. |
</> |
I think its worth targetting mid-level IR/AST representation rather than IL or just generating source code directly. |
Completely agree. Myriad is a better way of handling all this. |
@Krzysztof-Cieslak I think what this suggestion wants is some form of generating code or types that is better than the current state of type providers. I agree that generating strings is not the best approach. Myriad can be in the mix of things to discuss here, as is an upgrade to type providers, as is something similar to C# source generators. If there is any link to a high-level write-up of Myriad it would be useful so it can be considered. I can't find anything online apart from this blog about its development process. |
@kerams the Source Generators feature is currently in a very early preview. There's extensive design-time support planned, which you can read the beginnings of here: https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md#ide-integration. There's some interesting challenges to solve that are not unlike what Type Providers struggle with today. For example, you want generated source to be up to date, so the safest thing to do is regenerate on every keystroke. This is effectively what Type Provider do today since they're asked to provide "fresh" types whenever the language service needs to re-typecheck things. The upside is correctness, but the downside is a huge hit to design-time performance. We somewhat work around this today in Type Providers with a series of caches that were added in the VS 2019 16.0/16.1 timeframe. They also serve as a band-aid around some more fundamental architectural flaws that force the TPSDK to hunt for and load big binaries in memory (when the compiler already has all the data it's looking for), leading to large Large Object Heap (LOH) allocations that ultimately kill IDE perf. Currently the C# Source Generators simplify a lot of things by generating files in memory. But that offers some downsides; namely, not much C# tooling has a good understanding of them today. So there's a lot of design work that will probably go back and forth between compiler and IDE design until something acceptable emerges. Any F# implementation would likely utilize a similar mechanism once it stabilizes. |
As for the suggestion: I think this is something we'll want to wait a bit for. Firstly, C# Source Generators are just in their first preview and could undergo complete design overhauls between previews based on feedback and scenarios that become more apparent. The current experience exists mostly so that early adopters can try things out, see what is missing or needs to change, and let the team know how it needs to change. There's also a lot of work to do in good IDE integration. Finally, although the string concatenation approach is extremely flexible, it's not necessarily going to stick or be the only way to do things. I personally prefer it to having to learn some complicated API with its own set of bugs and design flaws, but I could see how others would feel the opposite considering that there's pretty much no guarantees around correctness when you're just concatenating strings. However, I suspect we'll eventually want to implement something that can "hook in" to the libraries that will ultimately end up using Source Generators. The blog post hints at some Microsoft frameworks and libraries adopting them. Realistically that won't happen for quite a while, largely because none of the "adopt source generators" work was costed for the .NET 5 timeframe. Perhaps an early prototype will emerge if Source Generators stabilize early enough. But in the long-term, I expect a lot of the .NET ecosystem to offer a "Source Generator path" for performance and better AOT-compilability. For F# to take part in these benefits, the F# compiler would also need a compatible feature. When the time comes, I think we'll likely look at a variety of options:
Table stakes would be ensuring that what is emitted can be consumed by .NET components so that F# developers can partake in the performance and AOT-ability gains. Post-.NET 5 it is highly likely that the .NET runtime team will focus heavily on AOT since it addresses a lot of pains people have with using .NET in production. This naturally means that some of heavy reliance on reflection in the .NET ecosystem may need a replacement. A Source Generator-like mechanism could be a key part of that for F#. Seriously considering these things is at least a year away though. |
@charlesroddie if there's interest I'm happy to write more about how to use Myriad / how to create plugins for Myriad etc. |
i think the proper way is to do it like nemerle macros. |
@OnurGumus I don't think we'll end up supporting syntactic macros: #210 |
Hi everyone, thanks for considering this. I want to be clear where I stand: I don't think that Source Generators are a wündertool of meta programming. I do think it's a very practical solution to a very real problem. I appreciate everyone wanting to do original research on hygienic macros, but this suggestion is specifically not that. :-) My thoughts on the above criticisms:
Closing thoughts: The C# ecosystem is about to be flooded with Source Generators. Already F# lags behind C# in tooling support. For example, Xamarin supports code generation for CoreML for C# but does not offer it for F#. The same is true for Storyboard support and XIB support and XML code behind. With every year, F# tooling falls behind C#. It is my opinion that it would benefit the F# community greatly to make writing tooling for F# easier. |
@praeclarum Just a note about tooling, I think that view is quite Xamarin-focused and not representative of where most developers are. Xamarin is heavily C#-focused today, in large part due to how project integration tooling works, since it uses the so-called "legacy" project system and flavoring. This old technology is feature-rich but inflexible. Most of the .NET Core/Standard-based stuff uses a different, far more flexible system that has led to tooling that is about as equally available for F# as it is for C#. One example is the Azure-based tooling that is equally available for F# projects. Additionally, some of the excellent API design of things like ASP.NET Core has allowed for more F#-friendly entry points to emerge (ASP.NET Core supports F# async without requiring conversion to task, Giraffe and Saturn build directly atop its abstractions, etc.) Perhaps future Xamarin components can be designed in a more pluggable way, like ASP.NET Core, and not require things like the enormous amount of work that was required to light up Fabulous (which also cannot plug into lots of the Visual Studio-based tooling). I anticipate it being easier to support F# in the future with Xamarin with the team moving their project integration tooling to the same system that .NET SDK-style projects use. Tooling for consuming source generators written in C# is also something to consider, and this would fall square in the "F# team that does tooling" realm to implement. I expect this to be important as more are available. |
I will 100% concede that I work on an uncommon platform compared to the rest of the community, but I hope you'll welcome diverse perspectives. Plus, Microsoft states that Source Generators are the recommended solution to the problems linkers present in .NET - a problem Xamarin devs have over a decade of experience with that is now becoming a very real problem in .NET Core.
I could have listed examples other than Xamarin. Protocol buffers could have been another example. I have been playing with a new version of sqlite-net that uses source generators (though I might end up with an IL masher to make it work with F#). I am also currently working on a library to assist mapping functional structures to object-oriented components (Fom) that could benefit from this technology. Anyway, thanks again for the consideration! |
One small thing should be noted: file ordering puts F# at a disadvantage vs C# regarding source generators (or Myriad). If I have a generator that, say, generates serialization code for annotated types, in F# I can't declare a type and use its serialization within the same file, because the generated code would need to be in-between. Whereas in C#, that's not a problem, there's just a cyclic reference between your file and the generated one. Type providers don't have this inconvenient either because they generate code at the right place.
I don't fully agree with your points here, but I still think that generating text is a good idea for a simple reason: it's much easier to cater to both preferences by making a helper library that provides an AST and generates text, than the other way around. |
@Tarmil Yeah, file ordering does limit what you could do with F# in that way. I think that scenario in particular would be confusing to never enable, but without doing something special like treating the file and the generated file as if their constructs were recursively declared, it would be the way things are. Another thing to consider is what supporting allowing one generator to depend on the output of another would look like. This implies another form of ordering, which I'm not particularly fond of given how top-down ordering is already difficult for beginners to grok. |
Interesting point about file ordering @Tarmil . Type providers have better safety than source generators as you can provide them with the input directly. They don't analyze your entire source (unless you are crazy enough to point them at your .fs files) and you only use the results in the places you specify. They suit F# as a safer, more explicit language. Enhancements to type provieers mentioned here:
How much would remain if this work were done? |
Here it's a matter of interop because the economics don't support F#-specific solutions for everything. Can type providers be used in C#? Imagine we relegate erasing type providers to a historical footnote. Then you can use them by referencing F# projects. Could they be used directly? For example you write some annotation Can C# source generators be used in F#? Say you have ProtoBuf generator which takes the source for a type as input. Then from F# you have a type, compile it to IL, decompile it to C#, feed it to the source generator, get enlarged source as output, compile it to IL, and reference it. Feasible or too many steps? I agree with @praeclarum that we need to think hard about how people using these language features can create .Net solutions rather than language-specific solutions. |
Im happy Myriad was mentioned here, feel free to add any ideas, improvements, ideas etc to the issues: https://github.com/MoiraeSoftware/myriad |
One reason, I dislike text based generation is adding complexity of multi-pass compilation and adding to the performance bloat in the compiler. Another reason, I dislike is, F# being a white-space sensitive language, it will probably make incredibly harder to get source generation right. I feel Myriad provide a good base to build features on top of it and suggestion to pass types to TP is the way forward. |
When I was building Myriad I did think of removing the quotation aspect of Type Providers and instead have just AST input rather than quotations. I think quotations not quite mapping 1 to 1 over the F# language can be a big limitation with regards to generating source, especially as quotations transform the input into a quoted from and cannot represent types either. Myriad could be called as part of the compile chain as there is an input into the compiler accepting an AST. Currently it can be integrated via MSBuild or by calling it direct with the CLI tool. |
I hope we could deal with existing ASTs instead of creating more and more of them. |
There the typed and untyped last, the typed AST is not user constructible so it only really leaves the untyped one, which also has an entry path into the compiler and fantoms for turning back into F# source. The typed AST is only really currently useful for transpiling an F# cast to another language as it has no API for modification or construction. You can convert a quotation back to an AST other the process is not perfects as data is lost in the initial quotation literal process, programmatic quotation construction does not cover the whole F# language either so not ideal. |
Now that we're talking about AST... :)
|
btw, a lot of projects (MS Bond, protobuf, GraphEngine etc.) already have this code generation workflow by using custom MSBuild tasks. So I don't think the workflow is something new, but how the mechanism generates executable bits (source? AST? etc.) is to be carefully designed. I wrote both versions of codegen for GraphEngine (it generates millions of lines of code for modeling strongly-typed knowledge graph) -- the first version is done in C# with string concatenation and the coding/debugging experience is horrible. In the second version I came up with something pretty unique -- it's a meta-template system that generates code generators. I made rules that the meta templates must compile fine themselves, with the "holes" properly annotated. The meta generator then transforms the meta template into generators, which takes user input, and generate source code. If F# is indeed going to implement the source generators, I wish it is not a CodeDom style API |
@yatli Have a look at Myriad, I recently updated the readme to be a little more descriptive. |
Could some kind of typed AST API construction be the right way to continue? (I'm sure this needs to be approved in principal first, not just a new PR.) The untyped AST needs quite lot of work to be used (and is potentially even a bit dangerous: the parser should really understand everything, as F# is not side-effect-free language). |
@yatli Technically it takes an AST as input, then creates an AST fragment from it then translates that to source code, which is included in your project. It could take other things as input too, its just the current API is an input file/AST. |
There are issues in prominent projects (Like PInvoke.Net) where the authors are considering moving to a primarily Source Generator-driven workflow, which is concerning because those libraries then become inaccessible to F#. |
I agree, source generating p/invoke calls (like MiniDumpWriteDump for example) is better than manually writing the code and getting it all wrong, especially when you need to maintain an However I do have a nit about source generating MiniDumpWriteDump at the moment and that is because:
|
A good discussion on source generators from the author of Dapper https://blog.marcgravell.com/2021/05/is-era-of-reflection-heavy-c-libraries.html |
I've been moving to .NET6. One of the major use cases of .NET6 for me is support for AOT compilation of Blazor to WebAssembly. As I understand it, this is currently blocked on F# lacking source generators for System.Text.Json, as reflection cannot be used with AOT compilation of Blazor. I feel that even if F# does not get source generators, there needs to be some interop story for using .NET apis which use source generators. |
I agree it would suck to require reflection in F# but at the same time for C# and VB to recommend moving away from it due to Source Generators providing a performance improvement due to using source generation to replace the need for reflection. After which time I feel like everything with reflection other than |
@pbiggar To be honest I've historically been sceptical of attempts to make .NET "AOT static" and especially when this is placed on the critical path for delivery. There are just so many .NET practices that require JIT code generation and/or a modicum of reflection - LINQ, generic virtual methods, a bunch of reflection things, some things in FSharp.Core. I've seen related agendas for .NET static compilation fail to deliver several times - delivering buggy, incomplete versions of things that look like .NET but aren't. In my eyes these shouldn't even be allowed to use the name .NET unless they actually implement the .NET standards (or minimal-expectations) including reflection. That said, I understand the .NET team are taking another stab at this, I'm not really following the technical details though. But the number of F# scenarios where static compilation to WebAssembly without any runtime codegen or reflection is critical seems small (and the number of C# scenarios where programmers don't end up using some technique or another requiring reflection is also small). That is, I don't currently see anyone across the F# world signing up to deliver this goal of end-to-end JIT-free, reflection-free execution of real-world F# code using rich libraries - both because it's difficult and and rarely necessary. The AOT compiler you're referring to does give some of this - and it's been available on Mono for a while including iOS execution with reflection - but adding the requirement to remove all libraries that use reflection is too onerous I think. So in this case I just don't think I'd encoourage F# users to sign up to that pat of the .NET 6 story - or if you do the responsibility is to understand that there are likely to be blocking points like the one you mention along the way, which require either C# code, or using a different reflection-free Json library. |
For the forseeable future that story will be "add a C# project and generate the source there". |
@dsyme There are very good reasons for the aot and linker friendliness plans in dotnet, so it's not reasonable to insist on the original spec without considering the benefits and costs of the changes. To deploy apps that run on end-user systems you want some form of AOT to avoid slow startup and JIT-lags and to avoid deploying code that is trivial to decompile. MonoAOT (for ios/android/wasm) is tolerant of reflection, but there is a lot of work being done on linking which could change this. NativeAOT is coming with much better size/performance characteristics, and this is less tolerant (with an optional reflection-free mode for minimum size). Reflection is overpowered, giving the ability to break any number of constraints. A lot of typical reasoning about F# code, particularly about types, has the qualification "in the absence of reflection". Therefore removing reflection creates a stronger form of F#, a safer version where constraints expressed by types are respected. Reflection usage outside of compilers is a code smell, and investigation into F# incompatibilities with corert/netnative picked out parts of F# that are badly written. These two aspects go together: the reason that reflection is performant-AOT-unfriendly and linker-unfriendly is that it limits the ability to reason about code. Overall the aot/linker moves (of which source generators are a part) are creating a superior form of dotnet, with performance benefits to end users but also enforcement of code constraints. It is a wide-ranging attempt to do dotnet properly and F# code should be the most naturally compatible as it favours type safety and explicitness over "magic". |
I do insist on conformance to the estabilished specs for .NET - both in ECMA and certainly since .NET Core. .NET isn't a play thing for its engineers to make what they want it to be on each iteration, it's a set of de-facto and actual standards. My fundamental belief is that .NET is simply not a statically compiled system (instead it's "majority statically compiled with occasional JIT or slow fallback-code") and anyone who thinks it can be is making a highly expensive category error. The .NET 1.0/2.0 addition of both reflection and generic-with-code-expansion-and-generic-virtual-methods (and the very widespread use of both of these) ensured that it never will be. Now, maybe "this time it's different" but, to put it another way, the costs are indeed not being properly considered. The costs of breaking established specs are vast, staggering, colossal. Probably about a trillion dollars of IT investment sits on top of .NET in one way or another - much of it on .NET Core. For example, there is zero costing for modifying F# for this, let alone all its libraries, let alone the broader universe of .NET libraries (e.g. the cost of revamping FSharp.Core to be reflection-free, possibly breaking into pieces, annotating it... ) As an aside, while I'm no fan of reflection from a soundness perspective, the theoretical benefits of F# for this kind of work are too theoretical for me to find compelling, given there's no costing here.
Sure, it's useful, but I still believe it's basically a pipe-dream to ever think you can have the majority of .NET coding be without JIT and reflection. It ain't going to happen. At best you end up with some niche tool for constrained-coding-app-on-device scenarios. However the programming model ends up so compromised (whether attributes, annotations, code-generation-tools, configuration files) beyond hello-world demonstrators that it's likely just some weird version of .NET that relatively few people know. At best, it bi-furcates the .NET ecosystem (as we saw with .NET for Silverlight, Windows Phone, .NET Native, whatever ....). Anyway, I want to emphasise that JIT-free .NET execution hasn't featured in our planning for people employed directly to work on F# at Microsoft.
More positively - from the F# perspective I'd be really grateful if the community lead on this - for example, why not create a tooling RFC under https://github.com/fsharp/fslang-design/tree/main/tooling where you can together document and record the whole matter of F# and jit-free AOT? That would create a single point of reference. A comparable RFC is this one: https://github.com/fsharp/fslang-design/blob/main/tooling/FST-1033-analyzers.md |
Also I disagree, source generator support should not break anything at all, only just gives a specific set of developers (myself included) another tool in our tool chest. Besides I do not think people wanted FSharp.Core to stop using reflection but rather used it as an example where some of it's reflection can be replaced better and be more performant than just using the reflection. I have put in place (even though I am an open source developer relying on my (currently 5$/month) donations to write and ship complete and open source .NET projects (all targeting .NET Standard 2.0) that can be used for everyone (even companies) just so they do not need to reinvent the wheel and end up doing it themselves which saves them time and money already (installing a nuget package ~10 seconds of their time nowdays). While I understand your argument that it can cost billions of dollars in costs, look at the open source developers like me who rely on donations and do not explicitly on our licenses say "To use this open source code you must donate at least x$ per month." just because we don't want to chase our user-bases away because they might be other people like me, making their own libraries that depends on some of my code to do their stuff which then they ship as a package (yet again) for others to consume as well. At this point in time I think the most the "money" argument is just that, an argument to try to justify not giving someone a tool in their toolchest to optimize for performance. I remember when source generators did not exist at all for Visual Basic and C# and look at the performance tank back then, System.Text.Json was over 30x slower than it is now, ASP.NET Core was about 40x slower in some reflection spots, System.Windows.Forms had the same issues as well, WPF also (all written in C# with the exception of WPF that contains some C++ bits). My point is, if they had that argument like you guys claim when specing for C# and Visual Basic source generators on roslyn back then chances are Source Generators would have never existed there and then those improvements would have never been made, ASP.NET would have been at the same performance level as the one from before Source Generators became an actual thing. I argue that source generators are being used to actually augment code that can be used in the place of reflection in some situations (if not all of them).
While that may be true, not all .NET Developers (even in the @dotnet organization and maybe also in the @fsharp organization here on github) do not work for Microsoft. Not all of them thing that JIT-free .NET execution is a must for some targets (for example what if you want to use F# to make an iOS application for an older iPhone that explicitly bans any form of JIT in order to run any apps) While iOS 15 might allow it somewhat (unless you apply to get it into the Apple App Store as it's still banned there) Some companies still face what us non-company bound programmers face everyday, WHEN you want to actually ship to the Apple AppStore (that means you cant use reflection anywhere in the execution phase at runtime and no JITer must be invoked as well to be accepted) making your application denied and a nonrefundable loss of 100$ each time to try to push that application (written in F#) up to the AppStore for Apple to look at and then use it to fund the whole project. Some open source developers actually do that with the C# made projects that made it into the AppStore thanks to these few things:
After that is all said and done they eventually push it to the AppStore and apple is then happy and approves it. The same is done at the company level. (You think none of the iPhone apps in the AppStore is not made in C#, I know for like Git2Go that was on my iPhone 5s that used libgit2sharp so that tells you something, they used C# and I think it. Other git based phone applications are also made in C# say for example the Github app for iPhones I think is made in C# and uses libgit2sharp as well. And ironically @github is owned by @microsoft. I think that applications for iPhones for example should not be limited to just C#, or Visual Basic.NET, but also include F# but for real world applications, source generators is a must. Hello-world applications and foo-bar applications are just examples of valid real world applications, but they do not properly benefit being source generated at all just because they normally do not use any reflection anyway and so no gains are made at all if you do or do not use source generators there. Infact it might be why you guys are thinking that it's not worth it but it's not entirely true, besides on the runtime side (the runtime team for .NET which works at @microsoft) also think that any place that does not use reflection in the runtime is a critical performance increase that should be made because then all users of .NET would be happy for a mor performant .NET. Heck that was the main argument between .NET Core (back when .NET Core was not rebranded to .NET) and .NET Framework back then because .NET Core optimized .NET Framework apis further and with it made performance increases of .NET Framework 4.x and made a valid reason for non-company based programmers, and company based ones to jump into .NET Core and use that instead of .NET Framework (where performance is critical because you process a lot of data and need good performance to reduce CPU load while not reducing the rate of processing data so then everyone is happy). This is why everyone uses things like I would also like to take some time to mention Rick Brewser (creator of Paint.NET) who might agree with me on all of this @rickbrew. |
Discussion has moved beyond source generators so I will just make a brief reply to @dsyme with links.
Last writeup from 3 years ago is dotnet/corert#6055 (comment) . NativeAOT will be more compatible now (@kant2002 has tried it recently) and at some point I will do an up to date summary. The largest issue is string functions documented here: #919 . That will unblock running the test suites (dotnet/fsharp#5340). F# mostly supports AOT by virtue of the compiler being mostly sensible. I think this can largely done by the community with some helpfulness from the F# team: willingness to take small behavioural changes, treat the ability to run performance F# apps on user devices as having some importance, not repeat the F#/UWP debacle.
F# apps are already running without JIT and with minimal reflection across iOS, Android, Windows and Mac devices, via MonoAOT and netnative. Very soon they will be running on web browsers via wasm. Really almost no constraints for writing AOT-friendly apps in F#. The nugets we want to use all work, and one just needs to take care about string functions in practice. You will get much more potential for nuget incompatibilites resulting from source generators. |
Regex source generators
Usage example: https://www.meziantou.net/regex-source-generator.htm // The Source Generator generates the code of the method at compile time
[RegexGenerator("^[a-z]+$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
private static partial Regex LowercaseLettersRegex();
public static bool IsLowercase(string value)
{
return LowercaseLettersRegex().IsMatch(value);
} |
[EventSource]
[GeneratedRegex] // renamed from [RegexGenerator] in 7.0 rc1
[JSExport]
[JSImport]
[JsonSerializable]
[LibraryImport]
[LoggerMessage] are provided by runtime libraries, and they avoid aot-incompatible reflection apis. |
I looked at some of the samples for C# source generators and they seem to be a C# and not a .NET feature as in the sample implementations they are literally doing C# source string concatenation. With that in mind I see it as a fool's errand to attempt to expose source generators without ever having to look at C# code. The two general cases I see is code augmentation of partial classes (e.g. automatic json serializers) and total generation of values that implement a known type signature (classes / interfaces / functions). Total Generation from Type SignatureFor this case I think we can take some inspiration from the way Fable does native (JavaScript) interop: // The member name is taken from decorated value, here `myFunction`
[<ImportMember("my-module")>]
let myFunction(x: int): int = jsNative
[<Import("DataManager", from="library/data")>]
type DataManager<'Model> (conf: Config) =
member _.delete(data: 'Model): Promise<'Model> = jsNative
member _.insert(data: 'Model): Promise<'Model> = jsNative
member _.update(data: 'Model): Promise<'Model> = jsNative
I would propose that For the [LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str); would become: [<LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)>]
let internal ToLower(str: string):string = csNative The // The Source Generator generates the code of the method at compile time
[RegexGenerator("^[a-z]+$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
private static partial Regex LowercaseLettersRegex(); becomes [<RegexGenerator("^[a-z]+$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)>]
let private LowercaseLettersRegex(): Regex = csNative Code Augmentation of Partial ClassesIn this case as @dsyme suggests here we have a ready mechanism with type providers. e.g. for STJ source generation type internal SourceGenerationContext = CSharpProvider<"""
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
"""> The challenge here comes where A much simpler case would be where type WeatherForecastOuter = CSharpProvider<"""
public class WeatherForecastOuter
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
}
"""> We could even embed C# syntax highlighting and auto complete as is done for html templates with Fable.Lit but that's certainly not required and is probably mostly an editor-level feature rather than a compiler feature. |
@roboz0r - I admit I don't fully understand your proposal so I am assuming you already considered it but here comes my question: With C# source generators the C# classes are generated as partial classes which allow the user to inject behavior using partial methods. So if a source generator generates C# and it's injected into F# as IL how can I use partial methods to inject behavior in the generated code? |
@mrange The idea is that anything to do with I think the order to create something like this would be:
In this first case the embedded C# is entirely unaware that it lives within an F# project, and F# has no input to what happens in C#-land besides expecting that some IL will pop out eventually. The "Total Generation from Type Signature" case is a bit of a departure from this ideal but considering that type signatures and attributes are entirely static they should be far easier to forward / transpile into C#.
2, 3, and 4 could be a long way away and potentially in a different order but if 1 can be completed that gives us a way forward to consume the most obvious use cases for source generators so far without major changes to F#. |
is this coming soon? Since the big support now for source generators for many things in C#, like e.g. json serialization for example, https://github.com/amis92/csharp-source-generators should be good to have this compatibility in F# too?
|
No, it's hasn't been planned yet, and will definitely not going to make it in 8. Don't get me wrong, we want to have it, but it's a huge feature. Designing it alone will probably take months, since all source generators rely on roslyn (and C#-only features), and will likely involve us using roslyn one way or another. We will also have to sort VS story out, they're slow enough natively in roslyn, and we will need to account for it too. We're not even sure how to approach it, since there are multiple existing flavors as well as new versions which are being designed now. Latest thoughts are here: dotnet/fsharp#14300 |
This one would also be great to interop with virtual actor framework Orleans, which would make F# interesting for BEAM/Elixir/Erlang otp/Gleam users ? From what i undersand Orleans relies on source generators for grain interfaces > https://learn.microsoft.com/en-us/dotnet/orleans/grains/code-generation?pivots=orleans-7-0 |
Support Source Generators
Add support similar to C# Source Generators
The idea is to execute the compiler in two passes:
The existing ways of approaching this problem in F# are:
Pros and Cons
The advantages of making this adjustment to F# are an easy form of meta programming. It's basically all the benefits of type providers without the complexity.
The disadvantages are the repetition of a feature and the compiler performance penalty of executing the type checker twice when this feature is used.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M (depending on what data is passed to the generators)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
The text was updated successfully, but these errors were encountered: