Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

f#

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?

like image 839
Daniel Avatar asked Jul 22 '10 16:07

Daniel


3 Answers

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
like image 86
Tomas Petricek Avatar answered Nov 03 '22 21:11

Tomas Petricek


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.

like image 23
Noldorin Avatar answered Nov 03 '22 21:11

Noldorin


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.).

like image 20
kvb Avatar answered Nov 03 '22 20:11

kvb