3
type TypeA () = class end
type TypeB () = inherit TypeA ()

// "The type 'TypeA' does not match the type 'TypeB'":
let iDict : IDictionary<TypeA, bool> = [ TypeB (), true; TypeB (), false ] |> dict

let d = Dictionary<TypeA, bool> ()
// This is OK, though:
d.Add (TypeB (), false)

Why are IDictionary keys incompatible with derived types?

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
MiloDC
  • 2,373
  • 1
  • 16
  • 25
  • 4
    Bound to be a duplicate, but arrays in F# are not covariant - method overloads like add are though – John Palmer Jul 17 '16 at 02:07
  • 3
    Because `KeyValuePair<'Key, 'Value>` is not covariant on `'Key`? – ildjarn Jul 17 '16 at 02:07
  • Of interest: [Eric Lippert's Erstwhile Blog - keyword: Covariance and Contravariance](https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/) Note: The list is in reverse chronological order, so start at the end and work forward. – Guy Coder Jul 17 '16 at 12:44
  • Of interest: [how to solve type constraint mismatch, C# to F#](http://stackoverflow.com/q/23018081/1243762) – Guy Coder Jul 17 '16 at 12:46

1 Answers1

2

Let's look at the differences between the two approaches:

In the first case, you are creating a list:

[ TypeB (), true; TypeB (), false ]

This has type (TypeB * bool) list.

The dict function has type seq<'Key * 'Value> -> IDictionary<'Key,'Value> (requires equality).

Consequently, applying the dict function

dict [ TypeB (), true; TypeB (), false ]

results in a value of type IDictionary<TypeB, bool>.

IDictionary<TypeB, bool> is not equivalent to IDictionary<TypeA, bool>, these are completely different and incompatible types, hence the compiler error.

If you wanted to initialise your dictionary from a collection of more derived types in this way, you'd have to do the upcast explicitly, e.g.:

let iDict = 
    [ TypeB (), true; TypeB (), false ] 
    |> List.map (fun (a,b) -> (a :> TypeA), b) 
    |> dict

In your second example, this problem never materialised because you originally created a Dictionary<TypeA, bool>.

You then use the Add method to add something of TypeB to the dictionary. Since Add is a method, F# can perform automatic upcasting on the argument, such that your value of TypeB is upcast to TypeA automatically.

TheInnerLight
  • 12,034
  • 1
  • 29
  • 52