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
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.
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.
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.
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.
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With