Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement an extension method for Generic.Dictionary in F# (type constraints) [duplicate]

Tags:

f#

Possible Duplicate:
Type extension errors

I would like to add an extension method in F# to System.Collections.Generic.Dictionary. The trouble is that I cannot seem to get the type constraints right. I was hoping something like the following would work:

type Dictionary<'k, 'd when 'k : equality> with

   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

However, the compiler complains that my declaration differs from that of Dictionary. It does not produce that particular error when I leave out the equality constraint. But then it warns that it's missing. Many thanks for any hints, preferably other that "turn the warning level down" :-)

EDIT

Many thanks to KVB for providing the answer I wanted.

type Dictionary<'k, 'd> with

static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =

    let res = new Dictionary<'k, 'd> (EqualityComparer<'k>.Default)
    for (k, d) in xs do
        res.Add (k, d)
    res

EDIT: Here is an example to better explain my reply to RJ. It shows that type arguments are optional when instantiating a type provided the compiler can infer them. It compiles without warnings or errors.

type System.Collections.Generic.Dictionary<'k, 'd> with
   static member test (dict:System.Collections.Generic.Dictionary<'k, 'd>) : bool =
        dict.Values |> List.ofSeq |> List.isEmpty


let test (x:System.Collections.Generic.Dictionary<'k, 'd>) =
    System.Collections.Generic.Dictionary.test x
like image 493
user1878761 Avatar asked Dec 05 '12 10:12

user1878761


People also ask

What is Extension Method in MVC?

What is extension method? Extension methods in C# are methods applied to some existing class and they look like regular instance methods. This way we can "extend" existing classes we cannot change. Perhaps the best example of extension methods are HtmlHelper extensions used in ASP.NET MVC.

Where we can use Extension Method in C#?

You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself.

Where do you put extension methods?

An Extension Method should be in the same namespace as it is used or you need to import the namespace of the class by a using statement. You can give any name of for the class that has an Extension Method but the class should be static.


2 Answers

For some reason the names of the type parameters have to match - this works fine for me

open System.Collections.Generic
type Dictionary<'TKey, 'TValue>  with
   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

I have no idea why this is the case (30 second look at the spec provides no clues either).

Update - the error is actually when the Dictionary parameters are the same as what is written in the method - doing

type Dictionary<'a, 'b>  with
   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

works just fine. This actually now makes sense. When the parameters are the same, there is an additional unspecified constraint - 'k:equality due to the new Dictionary<'k,'d>. However, for some reason, we can't put constraints in the extension definition (avoiding duplication?) so there is an error.

like image 98
John Palmer Avatar answered Sep 20 '22 02:09

John Palmer


If you need ofSeq functions for various collections, you might consider an approach similar to C# collection initializers. That is, make it work for any collection with an Add method. This also sidesteps your present problem.

open System.Collections.Generic
open System.Collections.Concurrent

module Dictionary =
  let inline ofSeq s = 
    let t = new ^T()
    for k, v in s do
      (^T : (member Add : ^K * ^V -> ^R) (t, k, v)) |> ignore
    t

module Collection =
  let inline ofSeq s = 
    let t = new ^T()
    for v in s do
      (^T : (member Add : ^V -> ^R) (t, v)) |> ignore
    t

open Dictionary

let xs = List.init 9 (fun i -> string i, i)
let d1 : Dictionary<_,_> = ofSeq xs
let d2 : SortedDictionary<_,_> = ofSeq xs
let d3 : SortedList<_,_> = ofSeq xs

open Collection

let ys = List.init 9 id
let c1 : ResizeArray<_> = ofSeq ys
let c2 : HashSet<_> = ofSeq ys
let c3 : ConcurrentBag<_> = ofSeq ys

Interestingly, you can even limit it to collection types with a specific constructor overload. For example, if you wanted to use structural equality you could do:

let t = (^T : (new : IEqualityComparer< ^K > -> ^T) (HashIdentity.Structural))
like image 34
Daniel Avatar answered Sep 19 '22 02:09

Daniel