Unexpected behaviour of static abstract members in F#7
See original GitHub issueThe compiler fails to detect an implemented static member that is the implementation of a static abstract member of an implemented interface, in an unexpected fashion.
Repro steps
The following code shows the behavior.
type App<'F, 'a> = App of obj
type IFunctor<'F when 'F :> IFunctor<'F>> =
static abstract fmap<'a, 'b> : ('a -> 'b) -> App<'F, 'a> -> App<'F, 'b>
type FList =
static member wrap (xs : list<'a>) : App<FList, 'a> = App xs
static member unwrap (App xs : App<FList, 'a>) : list<'a> = xs :?> _
interface IFunctor<FList> with
static member fmap<'a, 'b> (f : 'a -> 'b) (v : App<FList, 'a>) : App<FList, 'b> =
FList.wrap (List.map f (FList.unwrap v))
// This fails compilation with
// "error FS0039: The type 'FList' does not define the field, constructor or member 'fmap'."
let mapper1 f = FList.fmap f
// This works just fine.
let private fm<'F when 'F :> IFunctor<'F>> f = 'F.fmap f
let mapper2 f = fm<FList> f
Expected behavior
I would expect reference to FList.fmap
in mapper1
to work. And, if it doesn’t work, then at least I’d expect fm<FList> f
in mapper2
to fail as well.
Actual behavior
mapper1
fails compilation with “error FS0039: The type ‘FList’ does not define the field, constructor or member ‘fmap’.” but mapper2
compiles (and runs).
Known workarounds
None.
Related information
F#7/net7.0 build.
Issue Analytics
- State:
- Created 6 months ago
- Comments:8 (4 by maintainers)
Top Results From Across the Web
Static abstract members in interfaces - .NET
NET 6 breaking change where `static` interface members can now be marked ... process the associated metadata and have unexpected behavior.
Read more >Static abstract methods in interfaces - C# 11.0 draft feature ...
There is currently no way to abstract over static members and write generalized code that applies across types that define those static members....
Read more >[Open issue]: static abstract interfaces and static classes · ...
With static abstract interface methods, consider: using System; public class C : IMethods { public static void M() { } } internal interface...
Read more >Why can't I have abstract static methods in C#? - ...
A call to a static method is done through the class name, not through an object reference, and the Intermediate Language (IL) code...
Read more >Static Program Analysis
Static program analysis is the art of reasoning about the behavior of computer programs without actually running them.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I am going to close this as by design, please refer to RFC: https://github.com/fsharp/fslang-design/blob/main/FSharp-7.0/FS-1124-interfaces-with-static-abstract-members.md#alternatives, and specifically the conlusion for the implementation - https://github.com/fsharp/fslang-design/blob/main/FSharp-7.0/FS-1124-interfaces-with-static-abstract-members.md#conclusion
If you’d like, you can create a language suggestion for implementing a specific syntax (
(MyRepeatSequence :> IGenNext<MyRepeatSequence>).Next(str)
), as described in https://github.com/fsharp/fslang-design/blob/main/FSharp-7.0/FS-1124-interfaces-with-static-abstract-members.md#option-d---give-some-kind-of-explicit-call-syntax@En3Tho Thank you for reply! I think I get the picture. What irks me about the F# design choice is not that I have to be explicit about the interface implementation. The questionable design, in my view, is that
ListF.fmap f
does not work if only the interface is implemented. I would understand such a behavior for an instance member, because then you can actually explicitly cast the instance to the interface, i.e., do(myObj :> IMyInterface).Method ()
. In the present situation, with a static method, there is nomyObj
to cast, so there is no explicit way of saying “consider ListF as an IFunctor<ListF> here”. Instead, you’re forced to do the arguably very non-explicit workaroundlet fm<'F when 'F :> IFunctor<'F>> f = 'F.fmap f in fm<ListF> f
.