2011-08-15 7 views
7

Poniższy typ rozszerzeniebłędy rozszerzeń Rodzaj słownik <'K, 'V>

module Dict = 

    open System.Collections.Generic 

    type Dictionary<'K, 'V> with 
    member this.Difference(that:Dictionary<'K, 'T>) = 
     let dict = Dictionary() 
     for KeyValue(k, v) in this do 
     if not (that.ContainsKey(k)) then 
      dict.Add(k, v) 
     dict 

daje błąd:

The signature and implementation are not compatible because the declaration of the type parameter 'TKey' requires a constraint of the form 'TKey : equality

Ale kiedy dodać ograniczenie daje błąd:

The declared type parameters for this type extension do not match the declared type parameters on the original type 'Dictionary<,>'

ten jest szczególnie tajemniczy, ponieważ następujące rozszerzenie typu nie ma ograniczenia i wor ks.

type Dictionary<'K, 'V> with 
    member this.TryGet(key) = 
    match this.TryGetValue(key) with 
    | true, v -> Some v 
    | _ -> None 

Teraz mam dziwne myśli: czy ograniczenie jest wymagane tylko w przypadku dostępu do niektórych członków?

Odpowiedz

4
module Dict = 

    open System.Collections.Generic 

    type Dictionary<'K, 'V> with 
    member this.Difference(that:Dictionary<'K, 'T>) = 
     let dict = Dictionary(this.Comparer) 
     for KeyValue(k, v) in this do 
      if not (that.ContainsKey(k)) then 
       dict.Add(k, v) 
     dict 

EDIT.

Zgodnie F# spec (14.11 Additional Constraints on CLI Methods)

Some specific CLI methods and types are treated specially by F#, because they are common in F# programming and cause extremely difficult-to-find bugs. For each use of the following constructs, the F# compiler imposes additional ad hoc constraints:

  • x.Equals(yobj) requires type ty : equality for the static type of x
  • x.GetHashCode() requires type ty : equality for the static type of x
  • new Dictionary<A,B>() requires A : equality , for any overload that does not take an IEqualityComparer<T>
+1

Nigdy bym do tego nie doszedł. Dlaczego inicjowanie słownika za pomocą porównywarki powoduje obejście ograniczenia równości? – Daniel

+0

Dzięki, desco. Muszę zostać na nocnym czytaniu. – Daniel

+0

@Daniel, podejrzewam, że czytanie specyfikacji późno w nocy prawdopodobnie miałoby odwrotny skutek niż pozostawanie w górze - przynajmniej dla mnie;) – Benjol

0

Problem polega na zastosowaniu metody Add. Jeśli użyjesz tej metody Dictionary<TKey, TValue>, F # wymusi, że TKey ma ograniczenie równości.

Po krótkiej zabawie nie jestem pewien, czy można zapisać tę metodę rozszerzenia. System typu F # wydaje się wymuszać na typie deklaracji metody rozszerzenia, że ​​nie ma żadnych dodatkowych ograniczeń niż typ pierwotny (pojawia się błąd przy każdym dodaniu ograniczenia equality). Dodatkowo typy wymienione w poszczególnych metodach rozszerzenia nie mogą różnić się od podanego typu. Próbowałem na wiele sposobów i nie mogę tego poprawnie uruchomić.

Najbliżej Doszedłem jest metodą non-extension następująco

let Difference (this : Dictionary<'K, 'T>) (that:Dictionary<'K, 'T> when 'K : equality) = 
    let dict = Dictionary() 
    for KeyValue(k, v) in this do 
     if not (that.ContainsKey(k)) then 
      dict.Add(k, v) 
    dict 

Być może inny F # ninja będzie w stanie udowodnić mi źle

+0

Yikes ... moje dziwne myśli są potwierdzone. Wypróbowałem to na kilka sposobów i to też nie jest dla mnie możliwe. – Daniel

2

miarę widzę następujący kod robi trick:

module Dict = 
open System.Collections.Generic 

type Dictionary<'K, 'V> with 
    member this.Difference(that: Dictionary<'K,'V2>) = 
     let diff = 
      this 
      |> Seq.filter (fun x -> not <| that.ContainsKey(x.Key)) 
      |> Seq.map (fun x -> x.Key, x.Value) 
     System.Linq.Enumerable.ToDictionary(diff, fst, snd) 
0

(EDIT:. CKoenig ma ładny odpowiedź)

Hm, zrobiłem nie od razu widać, jak to zrobić.

Oto rozwiązanie, które nie jest typem bezpiecznym i może stanowić szaloną inspirację dla innych.

open System.Collections.Generic 

module Dict = 
    type Dictionary<'K, 'V> with  
    member this.Difference<'K2, 'T when 'K2 : equality>(that:Dictionary<'K2, 'T>) =  
     let dict = Dictionary<'K2,'V>()  
     for KeyValue(k, v) in this do   
      if not (that.ContainsKey(k |> box |> unbox)) then   
       dict.Add(k |> box |> unbox, v)  
     dict 

open Dict 

let d1 = Dictionary() 
d1.Add(1, "foo") 
d1.Add(2, "bar") 

let d2 = Dictionary() 
d2.Add(1, "cheese") 

let show (d:Dictionary<_,_>) = 
    for (KeyValue(k,v)) in d do 
     printfn "%A: %A" k v 

d1.Difference(d2) |> show 

let d3 = Dictionary() 
d3.Add(1, 42) 

d1.Difference(d3) |> show 

let d4 = Dictionary() 
d4.Add("uh-oh", 42) 

d1.Difference(d4) |> show // blows up at runtime 

Ogólnie wydaje się, że nie może być w żaden sposób do ujednolicenia typów K i K2 bez również zmuszając je mieć taki sam ograniczenie równości mimo ...

(EDIT: Wygląda na to, nazywając w .NET który jest równość-przymus-agnostyk jest dobrym sposobem tworzenia słownika w przypadku braku dodatkowych ograniczeń)

+0

Skąd się bierze przymus?Czy jest to związane z użyciem 'Dodaj', jak wskazał Jared? Czy jest jakiś inny sposób, aby wiedzieć, kiedy takie ograniczenia są wymagane, inne niż informujący Cię kompilator? – Daniel

+0

Zobacz 14.11: http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html#_Toc270597662 – Brian

+0

najwyraźniej "nowy słownik" dodaje ograniczenie, co wyjaśnia, dlaczego 'ToDictionary' jest ok. – Brian