Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# type constraints and reflection

Tags:

f#

Is there any way to determine whether a given type parameter satisfies the F# comparison constraint through reflection?

I would suspect not, since the expression

typedefof<Set<_>>.MakeGenericType [| typeof<System.Type> |]

appears to yield no errors. Still, I would like to hear some authoritative opinion on this.

like image 384
eirik Avatar asked Jan 15 '13 00:01

eirik


1 Answers

Quoting from Don Syme's thorough post on equality and comparison constraints:

The constraint type : comparison holds if:

  • if the type is a named type, then the type definition doesn't have the NoComparison attribute; and
  • the type definition implements System.IComparable; and
  • any “comparison dependencies” of the type also satisfy tyi : comparison

The constraint 'T when 'T :> IComparable can be encoded in CIL and reflected upon, whereas neither is true of 'T when 'T : comparison.

Since the two constraints are not equivalent, marking comparable types with the IComparable constraint is a bit misleading since it would make it impossible to distinguish between the two using reflection.

There's a similar relationship between the equality constraint and IEquatable<_>.

EDIT

Jack's mention that the comparison constraint could be encoded in F# metadata prompted me to look at the metadata reader in PowerPack. It can be used to detect the constraint:

open Microsoft.FSharp.Metadata

let setEntity = FSharpAssembly.FSharpLibrary.GetEntity("Microsoft.FSharp.Collections.FSharpSet`1")
for typeArg in setEntity.GenericParameters do
  printfn "%s - comparison=%b" 
    typeArg.Name 
    (typeArg.Constraints |> Seq.exists (fun c -> c.IsComparisonConstraint))

Here's a contrived example that shows the disparity between implementing IComparable and satisfying comparison:

type A() = 
  interface IComparable with
    member __.CompareTo(_) = 0

[<NoComparison>]
type B() =
  inherit A()

type C<'T when 'T : comparison>() = class end
type D<'T when 'T :> IComparable>() = class end

let c = C<B>() //ERROR
let d = D<B>() //OK
like image 175
Daniel Avatar answered Oct 31 '22 15:10

Daniel