dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.94k stars 788 forks source link

Static abstracts implemented/inherited in another interface don't work #15992

Open gusty opened 1 year ago

gusty commented 1 year ago

Static abstract implemented/inherited in another interface doesn't work

Repro steps

open System
open System.Collections.Generic

type ISemigroup<'T> = static abstract op_AdditionAddition : 'T * 'T -> 'T

[<Interface>]
type NonEmptySeq<'t> =
    inherit IEnumerable<'t>
    abstract member First: 't

    interface ISemigroup<NonEmptySeq<'t>> with
        static member op_AdditionAddition (x: 't NonEmptySeq, y) =
            let unsafeOfSeq (seq: _ seq) =
                { new NonEmptySeq<_> with
                    member _.First = Seq.head seq
                    member _.GetEnumerator() = seq.GetEnumerator()
                    member _.GetEnumerator() = seq.GetEnumerator() :> Collections.IEnumerator }
            let append (source1: 'a NonEmptySeq) (source2: 'a NonEmptySeq) = Seq.append source1 source2 |> unsafeOfSeq
            append x y

Expected behavior

Compile

Actual behavior

..fsx(12,15): error FS1207: Interfaces inherited by other interfaces should be declared using 'inherit ...' instead of 'interface ...'

Then I try what the error message suggests:

open System
open System.Collections.Generic

type ISemigroup<'T> = static abstract op_AdditionAddition : 'T * 'T -> 'T

[<Interface>]
type NonEmptySeq<'t> =
    inherit IEnumerable<'t>
    abstract member First: 't    

    static member op_AdditionAddition (x: 't NonEmptySeq, y) =
        let unsafeOfSeq (seq: _ seq) =
            { new NonEmptySeq<_> with
                member _.First = Seq.head seq
                member _.GetEnumerator() = seq.GetEnumerator()
                member _.GetEnumerator() = seq.GetEnumerator() :> Collections.IEnumerator }
        let append (source1: 'a NonEmptySeq) (source2: 'a NonEmptySeq) = Seq.append source1 source2 |> unsafeOfSeq
        append x y

    inherit ISemigroup<NonEmptySeq<'t>>

Expected behavior

Compile

Actual behavior

..fsx(14,13): error FS0366: No implementation was given for 'static abstract ISemigroup.op_AdditionAddition: 'T * 'T -> 'T'. Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.

Known workarounds

Use C#

Related information

Microsoft (R) F# Interactive version 12.8.0.0 for F# 8.0

wallymathieu commented 1 year ago

Is this the same as the following C# code?https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQAwAIpwHQGED2ANoQKYDGALgJb4B2AzgNzJqZwAszKGW2AMlVoBHLiwDM6QRRIAnAGYBDMiXQBJAMokAtlQDmM/AFcADgB4AKgD5kAb2ToHbAGzoFAI3oUZSiunPoABUJDegAKfwAPABo/dABPAEouAF9xSVppeSUVVQA5OgBRLWMKOM0hC0t0EDYxStt7R38AMSoZT3QbdF0SCkZ0VKRBqAkpWUVlNU0dfSNjfNoikrKSCqtqtQWl0vL6pAB6fYcNbT0DE1MTmfP5wuKd1crrJDsh5DT0GXJ8GWB0LfuKzWcEsoUaDigAFZLgVaIYtLIFBQfhYQVUehRYfDEciZFFwZhoaosQjvLj0b0FgBxEi0WRUMgknE/fFIRyE1EUiitdoUBIbK5nOYA5a7cwgqKC2YXPJ3UWPcWWZ6vdkjNRMskoxXoGmYuGkpE/UL8gC8XI1hpkxq47OJ+uZMlq2F1FtxxvQZu6lLoNLpMgZrqNSQJavF6B5HU9GIjFGtIYkWBcssWgLFIMCwTCye2QM56GimzlD2BVUSBJV7Mc2dTCvThgYCjkJAA8nJyqEsHVtfRVgkCeyK5XK1AAOzoOkAd3+RdzirBbKHi4cPaEzt6gbx/aXjhXa712M1m4X28r7s9u5jxq3Q+Dx8rg2vmDH9fojZbbdWoQieDoZCRoUSW92UGB8kGQIA==

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public interface ISemigroup<T>
{
    static abstract T Plus(T x, T y);
}
public interface INonEmptySeq<T> : IEnumerable<T>
{
    T First { get; }
}
public interface ISemigroupNonEmptySeq<T> : INonEmptySeq<T>
//  ISemigroup<ISemigroupNonEmptySeq<T>>
{
}

public  record NonEmptySeq<T1>(
    Func<IEnumerator<T1>> getEnumerator,
    Func<IEnumerator> getNonGenericEnumerator,
    Func<T1> getFirst) : ISemigroupNonEmptySeq<T1>,ISemigroup<INonEmptySeq<T1>>
{
    public IEnumerator<T1> GetEnumerator() => getEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => getNonGenericEnumerator();
    public T1 First => getFirst();
    public static INonEmptySeq<T1> Plus(INonEmptySeq<T1> x, INonEmptySeq<T1> y)
    {
        INonEmptySeq<T1> unsafeOfSeq(IEnumerable<T1> seq)
        {
            return new NonEmptySeq<T1>(
                seq.GetEnumerator,
                seq.GetEnumerator,
                () => seq.First()
            );
        }

        return unsafeOfSeq(x.Concat(y));
    }
}
vzarytovskii commented 1 year ago

Is the same allowed for non-statics?

gusty commented 1 year ago

No, interfaces can't have non-static members, only statics. I think in C# they can have default implementations for instance members, but that's a different story.