Reputation: 16071
I was somewhat surprised when I realised that F#'s map
implements both IDictionary<'Key, 'Value> and ICollection<KeyValuePair<'a, 'b>>
considering that both supports mutation (add and remove) as part of the contract.
Looking at the implementation of map
it simply excepts when you try to cause mutation!
let map = [| (1, "one"); (2, "two") |] |> Map.ofArray
let dict = map :> IDictionary<int, string>
dict.Add(3, "three");;
the above code throws the exception:
System.NotSupportedException: Map values cannot be mutated. at Microsoft.FSharp.Collections.FSharpMap
2.System-Collections-Generic-IDictionary
2-Add(TKey k, TValue v) at .$FSI_0007.main@() Stopped due to error
which is as expected.
For an immutable collection to be able to expose itself as a mutable one only for it to throw exception when the consumer of that collection tries to cause mutation seems such a dangerous decision.
Am I missing something here?
Upvotes: 8
Views: 1302
Reputation: 19231
IsReadOnly
allows for the interface to be read-only even though it provides writable methods.
Upvotes: 6
Reputation: 243096
I think the main reason is that .NET does not have any interface that represents immutable (part of an interface of a) dictionary. This means that all .NET APIs have to take IDictionary<K, V>
as parameter, even if they only intend to read from the dictionary. So:
Implementing IDictionary<'K, 'V>
is pretty much the only way to make the F# immutable map usable as a parameter to any .NET libraries that need an object that supports lookup. Sadly, there is no read-only alternative in .NET.
Implementing ICollection<KeyValuePair<'K, 'V>>
does not make too much sense to me, because there is a read-only alternative IEnumerable<KeyValuePair<'K, 'V>>
and immutable map implements this interface too.
But perhaps there are some .NET libraries that take ICollection<'T>
(for efficientcy - i.e. to get Count
without enumerating all elements) and use it in a read-only way.
EDIT: As noted by Daniel in the comment, implementing ICollection
is required, because the IDictionary
interface inherits from it.
I think the lack of read-only interface is quite unfortunate, but there is probably no way this could be fixed, because it would mean changing the existing .NET collection libraries.
Upvotes: 9
Reputation: 7560
It's a .Net thing. You cannot implement only some of the interface, and yet they wanted to implement the IDictionary`2
interface - because you can use its querying and conversion methods (so you can pass it to functions that read from an IDictionary`2
).
Upvotes: 4