输入字典<'K,'V>的扩展错误

时间:2011-08-15 14:14:40

标签: dictionary f# extension-methods

以下类型扩展名

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:equality

形式的约束

但是当我添加约束时,它会给出错误:

  

此类型扩展的声明类型参数与原始类型“Dictionary&lt; &gt;”

上的声明类型参数不匹配

这尤其神秘,因为以下类型扩展没有约束并且有效。

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

现在我有一些奇怪的想法:只有在访问某些成员时才需要约束吗?

4 个答案:

答案 0 :(得分: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

修改

根据F# spec (14.11 Additional Constraints on CLI Methods)

  

一些特定的CLI方法和类型由F#专门处理,因为它们在F#编程中很常见,并且导致极难发现的错误。对于以下构造的每次使用,F#编译器都会施加额外的临时约束:

     
      
  • x.Equals(yobj)要求类型为ty : equality
  • 的静态类型x   
  • x.GetHashCode()要求类型为ty : equality
  • 的静态类型x   
  • new Dictionary<A,B>()需要A : equality,因为任何不带IEqualityComparer<T> 的重载
  •   

答案 1 :(得分:2)

据我所知,以下代码可以解决问题:

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)

答案 2 :(得分:0)

问题是您使用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#忍者将能证明我的错误

答案 3 :(得分:0)

(编辑: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

总的来说,似乎没有办法统一类型​​KK2而不强迫它们具有相同的等式约束但是......

(编辑:似乎要调用.NET,这是一种在没有额外约束的情况下创建字典的好方法。