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.87k stars 783 forks source link

Inheriting IComparable should satisfy a comparison constraint #11764

Closed kentcb closed 3 years ago

kentcb commented 3 years ago

The following does not compile:

type Wrapper<'T when 'T :> IComparable> = {
    Values: Set<'T>
}

It insists on a comparison constraint on 'T even though the documentation (and intuition) suggests that inheriting from IComparable is the same thing:

'T must implement IComparable, or be an array type with comparable elements, or be System.IntPtr or System.UIntPtr. Also, 'T must not have the NoComparison attribute.

It doesn't matter whether the constraint is for IComparable, IComparable<'T>, IStructuralComparable, or all of them.

Expected behavior

The code should compile.

Actual behavior

A type parameter is missing a constraint 'when 'T : comparison'

Known workarounds

cartermp commented 3 years ago

Sorry if I don't understand - is the following not sufficient for your needs?

type Wrapper<'T when 'T : comparison> = {
    Values: Set<'T>
}

There's no additional constraint if you do this.

kentcb commented 3 years ago

@cartermp Thanks for the reply. No, it's not because my real scenario is more like this:

type IMyBaseType =
    inherit IComparable
    inherit ISomething
    inherit ISomethingElse

type Wrapper<'T when 'T :> IMyBaseType> = {
    Values: Set<'T>
}

I don't want to have to constrain to both IMyBaseType and comparison because I feel like I'm repeating myself as IMyBaseType already inherits from IComparable.

DalekBaldwin commented 3 years ago

There are further requirements for a type to be comparison that are not mentioned on that page. From section 5.2.10 of the spec:

The constraint type : comparison is a comparison constraint. Such a constraint is met if all the following conditions hold: • If the type is a named type, then the type definition does not have, and is not inferred to have, the NoComparison attribute, and the type definition implements System.IComparable or is an array type or is System.IntPtr or is System.UIntPtr. • If the type has comparison dependencies ty_1, ..., ty_n, then each of these must satisfy ty_i : comparison

kentcb commented 3 years ago

@DalekBaldwin Thanks, but which of those requirements is not satisfied in my example?

DalekBaldwin commented 3 years ago

You can't guarantee the second bullet point will be satisfied for every type that could ever be created that implements IMyBaseType, e.g. with the standard pattern for propagating equality and comparison constraints through generic types:

type MyGenericType<[EqualityConditionalOn; ComparisonConditionalOn] 'T> (value:'T) =
    override this.Equals thatObj =
        // uses Unchecked.equals
        // ...
    override this.GetHashCode () =
        // uses Unchecked.hash
        // ...
    interface System.IComparable with
        member this.CompareTo thatObj =
            // uses Unchecked.compare
            // ...

There are a number of fslang-suggestions issues along the lines making these constraints more flexible, but as it is currently defined, comparison does not subsume IComparable.

dsyme commented 3 years ago

I'll close this, as it is indeed by design (@DalekBaldwin feel free to link through to the issues on fslang-suggestions)