fslang-suggestions
fslang-suggestions copied to clipboard
Imply class-level and module-level `[<Extension>]` attribute
I propose we not require a top-level [<Extension>]
attribute on a class when defining extension methods. Consider the following extension to IEnumerable<'T>
:
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
let f (x: IEnumerable<int>) = x.Sum()
Note that I must specify [<Extension>]
on both the method and the containing class, otherwise it won't compile because Sum
is not seen as an extension method.
It would be nice to just write the attribute for the method:
open System.Collections.Generic
open System.Runtime.CompilerServices
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
let f (x: IEnumerable<int>) = x.Sum()
Additionally, it should emit the Extension
attribute at the module level like C# does.
Pros and Cons
The advantages of making this adjustment to F# are:
- Less stuff to remember
- It's arguably more of a leaked implementation detail that the containing class must have this attribute
- More similar to C#, which emits the attribute
The disadvantages of making this adjustment to F# are:
- It's work
Extra information
Estimated cost (XS, S, M, L, XL, XXL): S
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- [x] This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- [x] I have searched both open and closed suggestions on this site and believe this is not a duplicate
- [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
- [x] This is not a breaking change to the F# language design
- [x] I or my company would be willing to help implement and/or test this
I thought you already had to do this to get visibility from C#?
Oh... misread, my mind remove the not keyword. Carry on...
I’m all for this. But while we’re at it, could this also include emitting the assembly-level extension attribute as well? This is needed for compatibility with VB. Effectively, stick [<assembly: Extension>]
somewhere... or else VB consumers lose out.
@pblasucci Poor VB users, I think nobody does this in F#. Is this even mentioned in the F# extension docs?
I'm all in for this too including what @pblasucci suggests. As F# users we know best how it feels to be left behind 😉
@matthid this is mentioned in the docs for F#. It's definitely got a use case: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/type-extensions#generic-limitation-of-intrinsic-and-optional-type-extensions
@cartermp Are you sure? I'm talking about the assembly level attribute. It is not part of any sample and not mentioned in the text (at least I don't see it anywhere)
In any case, this shows that the suggestion is reasonable ;)
Oh, assembly level attributes. No, nobody uses those.
@cartermp there are people defining F# extension methods to be called from VB.NET.
https://theburningmonk.com/2012/09/f-make-extension-methods-visible-to-c/
F# should do the same as C# in this regard and not treat VB.NET client code as alien, emitting the attribute on the assembly is necessary for this and should be part of the suggestion.
Adjusted
@cartermp I think it worth adding ExtensionAttribute
to F# native extension method types too by default.
Now you can write
type MyType with
[<Extension>]
member myType.MyExtensionMethod() = ()
But you can't attach the attribute to the generated class
[<Extension>]
type MyType with
[<Extension>]
member myType.MyExtensionMethod() = ()
So why not to do that implicitly by default for all members and a type itself?
for "simple F#" aim, since VB.NET and C# know about type extension, and defining those in those languages is a pain, and even worse in F# due to this feature not being baked in the compiler.
Normalizing it all, to F# type extensions, exposed as extension methods (implicitly, or with an attribute, which makes sense if we prefer the safer explicit route) makes a ton of sense to me.
This would be a really help for newcomers (like me) . I spent some time trying to figure out this one :)
We use it this heavily on Fabulous V2 new DSL, ie Entry. So this will reduce a lot of boilerplate :)
Great that you started working on this! Since this is a change to the language, I assume this needs an RFC? In particular, it should say something like this, to make sure we all agree on the rules of this change (plus probably a note on motivation and VB-compatibility):
- If an
Extension
attribute is present on a member (any member??) - Then F# will emit this on the containing class AND the containing assembly (and .NET module? @cartermp alludes to that, not sure it is needed?)
- If
Extension
attribute is already on the containing class, but not on the containing assembly, F# will emit it on the assembly. - If
Extension
attribute is already on the containing assembly, but not on the containing class, F# will emit it on the class - If
Extension
attribute is already on both the containing assembly AND class, it will not emit an extra attribute.
/cc @nojaf, @edgarfgp