Ошибки расширения типа для словаря <'K,' V> - PullRequest
7 голосов
/ 15 августа 2011

следующий тип расширения

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

выдает ошибку:

Подпись и реализация несовместимы, потому что объявление параметра типа 'TKey' требует ограничения формы 'TKey: равенства

Но когда я добавляю ограничение, оно выдает ошибку:

Объявленные параметры типа для этого расширения типа не совпадают с объявленными параметрами типа в исходном типе 'Dictionary <<em>, >'

Это особенно загадочно, потому что следующее расширение типа не имеет ограничений и работает.

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

Теперь у меня странные мысли: требуется ли ограничение только при доступе к определенным членам?

Ответы [ 4 ]

4 голосов
/ 15 августа 2011
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

Согласно F # spec (14.11 Дополнительные ограничения на методы CLI)

Некоторые специфические методы и типы CLI обрабатываются специально F #, потому что они распространены в F # программировании и вызывают чрезвычайно трудные для поиска ошибки. Для каждого использования следующих конструкций компилятор F # накладывает дополнительные специальные ограничения:

  • x.Equals(yobj) требует тип ty : equality для статического типа x
  • x.GetHashCode() требует тип ty : equality для статического типа x
  • new Dictionary<A,B>() требуется A : equality, для любой перегрузки, которая не принимает IEqualityComparer<T>
2 голосов
/ 15 августа 2011

насколько я вижу, следующий код делает свое дело:

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 голосов
/ 15 августа 2011

(РЕДАКТИРОВАТЬ: CKoenig имеет хороший ответ.)

Хм, я тоже не сразу нашел способ сделать это.

Вот не типобезопасное решение, которое можетобеспечить сумасшедшее вдохновение для других.

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

В целом может показаться, что невозможно объединить типы K и K2, не заставляя их иметь одинаковое ограничение равенства, хотя ...

(РЕДАКТИРОВАТЬ: кажется, что вызов в .NET, который не зависит от равенства является хорошим способом создать словарь в отсутствие дополнительного ограничения.)

0 голосов
/ 15 августа 2011

Проблема в том, что вы используете метод Add. Если вы используете этот метод Dictionary<TKey, TValue>, тогда F # принудит, что TKey имеет ограничение равенства.

Немного поиграв, я не уверен, что можно даже написать этот метод расширения. Система типов F #, по-видимому, заставляет тип объявления метода расширения не иметь никаких дополнительных ограничений, чем исходный тип (я получаю сообщение об ошибке всякий раз, когда добавляю ограничение equality). Кроме того, тип, указанный в методах индивидуального расширения, не может отличаться от указанного типа. Я пробовал несколько способов и не могу заставить это работать правильно.

Самое близкое, что я получил, - это метод без расширения следующим образом

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

Возможно, другой F # ниндзя сможет доказать, что я не прав

...