Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic constraint comparison is required despite type being restricted to INumber (generic math .NET 7) #14338

Open
gurustron opened this issue Nov 16, 2022 · 3 comments
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Needs-RFC Resolution-By Design
Milestone

Comments

@gurustron
Copy link

gurustron commented Nov 16, 2022

Repro steps

let inline test<'T when 'T :> INumber<'T>> x:'T = 
    if x > 'T.Zero then x
    else 'T.Zero

Actual behavior

Compilation error:

Severity Code Description Project File Line Suppression State
Error FS0193 A type parameter is missing a constraint 'when 'T: comparison' FSharpTests Program.fs 37 Active

Expected behavior

Compiles like C# counterpart:

public T Tets<T>(T x) where T : INumber<T> => x > T.Zero ? x : T.Zero;

Known workarounds

Add the constraint:

let inline test<'T when 'T :> INumber<'T> and 'T:comparison> x:'T = 
    if x > 'T.Zero then x
    else 'T.Zero

Related information

  • Operating system Win 11
  • .NET 7
  • Visual Studio 17.4
@github-actions github-actions bot added this to the Backlog milestone Nov 16, 2022
@T-Gro T-Gro added Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen labels Nov 18, 2022
@charlesroddie
Copy link
Contributor

This is a consequence of fsharp/fslang-suggestions#816 . FSharp equality and comparison badly need to move into the generic era.

@dsyme
Copy link
Contributor

dsyme commented Nov 18, 2022

This is by design - F# structural equality and comparison builds on IComparable, not IComparable<T>

If you want to enforce non-structural comparison throughout your code you can do this:

open FSharp.Core.Operators.NonStructuralComparison
open System.Numerics

let inline test<'T when 'T :> INumber<'T>> (x:'T) = 
    if x > 'T.Zero then x
    else 'T.Zero

However this means, for example, you can't use structural equality on F# lists or tuples or arrays.

If you need to mix of structural comparison and non-structural comparison, you can do this:

let inline (>.) x y = FSharp.Core.Operators.NonStructuralComparison.(>) x y
let inline (>=.) x y = FSharp.Core.Operators.NonStructuralComparison.(>=) x y
let inline (<.) x y = FSharp.Core.Operators.NonStructuralComparison.(<) x y
let inline (<=.) x y = FSharp.Core.Operators.NonStructuralComparison.(<=) x y
let inline (=.) x y = FSharp.Core.Operators.NonStructuralComparison.(=) x y
let inline (<>.) x y = FSharp.Core.Operators.NonStructuralComparison.(<>) x y

open System.Numerics

let inline test<'T when 'T :> INumber<'T>> (x:'T) = 
    if x >. 'T.Zero then x
    else 'T.Zero

@charlesroddie
Copy link
Contributor

That is interesting. How does that work? These operators don't work with IComparable<'T>.

open System
open NonStructuralComparison
type T() =
    interface IComparable<T> with member _.CompareTo(_:T) = 0
let compared = T() < T() // The type 'T' does not support the operator '<'

This one works on IComparable<'T> including INumber.

let inline (>.) (x: 'T) (y: 'T) = (x:> IComparable<'T>).CompareTo(y) > 0

@0101 0101 added Needs-RFC and removed Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. Needs-Triage labels Mar 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Needs-RFC Resolution-By Design
Projects
Status: New
Development

No branches or pull requests

5 participants