Bubble-up function attributes to `FSharpFunc<_,_>` so they can be used more easily
Is your feature request related to a problem? Please describe.
Hello, hope you're good. I'm taking a look at the available approaches for reading custom attributes from a function definition and eventually found this page from StackOverflow: https://stackoverflow.com/a/65157463/10146684.
The way of doing this, according to this answer from SO, demands the knowledge of the module that the function is defined, which does not really fit my needs.
Then, I thought that it could be more practical if the attributes defined for a specific function were "bubbled-up" to the FSharpFunc<_,_> instance, so we can directly retrieve those attributes from it.
Disclaimer: I don't know the real impact of this change, but at least in theory I think it makes sense.
Describe the solution you'd like
Replicate the function attributes at the FSharpFunc<_,_> instance to make it easier to retrieve this information for functions, using something like:
type MyAttribute(value: int) =
inherit System.Attribute()
member this.Value = value
[<My(42)>]
let myFunction() = ()
myFunction.GetType().GetCustomAttributes(true)
Describe alternatives you've considered
Additional context
Techinally a function in a module is a static method in a static class while fsharpfunc is a dedicated type.
This can create friction as there ateibutes that have strict attribute usage e.g. method or class etc.
You could bubble up some attributes to a synthesized type but not all of them due to these constraints.
I don't think it's a good idea tbh.
I believe there were proposals along of methodinfoof(...) in C# to simplify scenarios like these but they never got real traction.
F#9 did have big improvements in attribute targets, which would fail with this - I guess the expectation is to annotate the function like a method, but the runtime representation of the FsharpFunc is a class.
Yeah, we might need to think where to emit them - on type, on Invoke method, on method which has the same name as function? In all of those cases - do we "special-case" the attribute targets and widen them to be allowed on more targets than it (possibly) initially is? Also, a separate discussion is what do we do on anonymous funcions? Right now attributes aren't propagated anywhere from them, and I think it was strictly rejected as suggestion.
I think the goal of this would be to read the attributes of the method at the delegate target. I suppose the C# delegate equivalent of this would be .. GetMethodImpl or the Method property on delegate.
https://learn.microsoft.com/en-us/dotnet/api/system.delegate.getmethodimpl?view=net-8.0 https://learn.microsoft.com/en-us/dotnet/api/system.delegate.method?view=net-8.0
We could certainly implement something similar on the various FSharpFunc types and add the codegeneration to provide an override that gets the methodimpl for the necessary function. However, this means that every function value would cause us to generate an additional func, so we would probably want to measure the impact and make it opt-in.
An alternative would be as Vlad suggested, codegen the attributes on the generated Invoke method, which would also increase the size of the built assembly because the attributes would be duplicated, but only in the case of methods with custom attributes, which is likely better.
All in all I think I prefer attributing the invoke, but Method/GetMethodImpl would be a bit more reflection friendly.