Daniel
Daniel

Reputation: 47914

How to create a type that implement IDictionary<'K, 'V> and IEnumerable<'V>

I want to create a read-only keyed collection that implements IDictionary<'K, 'V> and IEnumerable<'V>. Taking the obvious approach I get the following error:

This type implements or inherits the same interface at different generic instantiations 'IEnumerable<'V>' and 'IEnumerable<KeyValuePair<'K,'V>>'. This is not permitted in this version of F#.

Is there a different way of achieving this?

EDIT - Since this seems to be an insurmountable limitation of F#, what would be an idiomatic way of achieving this? One thought that comes to mind is providing members that return the desired view of the data, e.g., member x.List : IList<'V> and member x.Dict : IDictionary<'K, 'V>. Object expressions could be used to provide the implementations. Any other ideas?

Upvotes: 7

Views: 494

Answers (3)

Tomas Petricek
Tomas Petricek

Reputation: 243096

One relatively easy approach is to expose the implementation of the two interfaces as members of the type you are writing. This can be done quite nicely using object expressions or just by writing a piece of code that constructs some type and returns it as the result. The second approach would look like this:

type MyCollection<'K, 'V when 'K : equality>(keys:list<'K>, values:list<'V>) =
  member x.Dictionary = 
    Seq.zip keys values |> dict
  member x.Enumerable = 
    values |> List.toSeq

The first approach (if you want to implement methods of the interfaces directly would look roughly like this:

type MyCollection<'K, 'V when 'K : equality>(keys:list<'K>, values:list<'V>) =
  member x.Dictionary = 
    { new IDictionary<'K, 'V> with 
        member d.Add(k, v) = ... }            
  member x.Enumerable = 
    // Similarly for IEnumerable
    values |> List.toSeq

Exposing the implementations as functions in a module as mentioned by kvb is also a great option - I think that many of the standard F# library types actually do both of the options (so that the user can choose the style he/she prefers). This can be added like this:

module MyCollection = 
  let toDict (a:MyCollection<_, _>) = a.Dictionary

Upvotes: 7

kvb
kvb

Reputation: 55185

As Noldorin says, this is not possible. One idiomatic approach is to provide toSeq and toDict functions on a module with the same name as your type (like List.toSeq, Array.toSeq, etc.).

Upvotes: 2

Noldorin
Noldorin

Reputation: 147471

I'm afraid not. The CLR allows implementation of multiple interfaces of course (even of the same base type), but not the F# language. I believe you won't have any problems if you write the class in C#, but F# is going to give you problems in the current version.

Upvotes: 3

Related Questions