2011-08-15 9 views
7

El siguiente tipo de extensiónerrores extensión de tipo de diccionario <'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 

da el error:

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

Pero cuando agrego la restricción que da el error:

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

Este es especialmente misterioso porque la siguiente extensión de tipo no tiene la restricción y wor Kansas.

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

Ahora estoy teniendo pensamientos extraños: ¿la restricción es necesaria solo cuando se accede a ciertos miembros?

Respuesta

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 

EDITAR:.

Según 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

Nunca hubiera llegado a eso. ¿Por qué la inicialización del diccionario con el comparador elude la restricción de igualdad? – Daniel

+0

Gracias, desco. Necesito estar despierto para leer algunas lecturas nocturnas. – Daniel

+0

@Daniel, sospecho que leer las especificaciones a altas horas de la noche probablemente tendría el efecto opuesto a permanecer despierto, para mí de todos modos;) – Benjol

0

El problema es su uso del método Add. Si usa este método de Dictionary<TKey, TValue>, entonces F # aplicará que TKey tiene la restricción de igualdad.

Después de jugar un poco, no estoy seguro de que sea posible escribir este método de extensión. El sistema de tipo F # parece forzar al tipo de declaración del método de extensión a no tener restricciones adicionales que el tipo original (obtengo un error cada vez que agrego la restricción equality). Además, el tipo enumerado en los métodos de extensión individales no puede diferir del tipo enumerado. Lo he intentado de varias maneras y no puedo hacer que esto funcione correctamente.

Lo más cerca que he llegado es el método no extensión de la siguiente manera

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 

Tal vez en otra F # Ninja será capaz de demostrar que estoy equivocado

+0

Uff ... mis pensamientos extraños se confirman. Lo he intentado de varias maneras y tampoco me parece posible. – Daniel

2

por lo que yo puedo ver el siguiente código hace el truco:

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 tiene una respuesta agradable)

Hm, lo hice No veo de inmediato una manera de hacer esto tampoco.

Aquí hay una solución no segura para tipos que podría proporcionar una inspiración loca para otros.

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 

En general, parece que puede haber ninguna forma de unificar los tipos K y sin K2 también los obliga a tener la misma restricción de igualdad, aunque ...

(EDIT: Parece que poner en .NET que es la igualdad en restricciones agnóstico es una buena manera de crear un diccionario en ausencia de la restricción adicional)

+0

¿De dónde viene la restricción?¿Está relacionado con el uso de 'Agregar', como señaló Jared? ¿Hay alguna otra forma de saber cuándo se requieren tales restricciones, que no sea el compilador que le dice? – Daniel

+0

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

+0

aparentemente 'new Dictionary' agrega la restricción, lo que explica por qué' ToDictionary' es De acuerdo. – Brian

Cuestiones relacionadas