fslang-suggestions icon indicating copy to clipboard operation
fslang-suggestions copied to clipboard

Imply class-level and module-level `[<Extension>]` attribute

Open cartermp opened this issue 5 years ago • 15 comments

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

cartermp avatar Feb 05 '20 17:02 cartermp

I thought you already had to do this to get visibility from C#?

7sharp9 avatar Feb 05 '20 18:02 7sharp9

Oh... misread, my mind remove the not keyword. Carry on...

7sharp9 avatar Feb 05 '20 18:02 7sharp9

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 avatar Feb 05 '20 18:02 pblasucci

@pblasucci Poor VB users, I think nobody does this in F#. Is this even mentioned in the F# extension docs?

matthid avatar Feb 05 '20 21:02 matthid

I'm all in for this too including what @pblasucci suggests. As F# users we know best how it feels to be left behind 😉

realvictorprm avatar Feb 05 '20 22:02 realvictorprm

@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 avatar Feb 05 '20 22:02 cartermp

@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 ;)

matthid avatar Feb 05 '20 22:02 matthid

Oh, assembly level attributes. No, nobody uses those.

cartermp avatar Feb 05 '20 22:02 cartermp

@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/

image

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.

smoothdeveloper avatar Feb 06 '20 00:02 smoothdeveloper

Adjusted

cartermp avatar Feb 09 '20 19:02 cartermp

@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?

xperiandri avatar Apr 19 '22 15:04 xperiandri

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.

smoothdeveloper avatar Apr 19 '22 15:04 smoothdeveloper

This would be a really help for newcomers (like me) . I spent some time trying to figure out this one :)

edgarfgp avatar Apr 29 '22 21:04 edgarfgp

We use it this heavily on Fabulous V2 new DSL, ie Entry. So this will reduce a lot of boilerplate :)

edgarfgp avatar Apr 29 '22 21:04 edgarfgp

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

abelbraaksma avatar Sep 05 '22 12:09 abelbraaksma