Question

There is a micro question, here about why an upcast is needed in the final answer I came up with (at the bottom of this); and a macro question about whether I am just missing the "elephant in the room:" some really obvious succinct way to do what I want [please don't ask me -why- I want what I do want; just take it as a given that I want this, and it is...]

I want to initialize a BsonDocument from F# via the MongoDB.Bson CLR assmbly. The particular overload of the BsonDocument constructor I think I should use is

MongoDB.Bson.BsonDocument.BsonDocument(IDictionary<string,object>)

and here is why I think this is the one I should use (the following is a long stroll through the garden of types ...)

The C# sample from the MongoDB site MongoDB CSharp Driver Tutorial uses collection-initializer syntax, which maps to one or more calls of .Add on the interface exposed by BsonDocument. The tutorial sample resembles the following:

var bdoc = new BsonDocument { { "a", "1" }, { "b", "2" }, };

I am not positive which overload of .Add is being used (and don't know how to check in visual studio), but all of the dictionary-based overloads are typed as <string, object>. In this case, the second values in each pair, namely "1" and "2", of type string, are automatically (by inheritance) also of type object, so everything is ok. And the other overloads of .Add that require the second item to be of type BsonValue, which is an abstract supertype of BsonString, which has an implicit conversion from .NET string no matter which overload is used; so everything is ok there, too. It doesn't matter which overload of the constructor is called.

This is a little difficult to maneuver into an F# equivalent because it's difficult to get at the .Add method of BsonDocument. I thought of

[("a", "1");("b", "2");] |> Seq.iter BsonDocument.Add

but that doesn't work because BsonDocument.Add is not a static method; I could instantiate the BsonDocument and then write a fun lambda that calls the BsonDocument's .Add method, which would at least isolate mutability to the fun:

[("a", "1");("b", "2");] |> Seq.fold ...

but this turns out to be super ugly, both due to the fun needing an explicit type notation on the BsonDocument because the variable referring to the BsonDocument occurs before the (new BsonDocument()), so left-to-right type inference doesn't have enough info (yet), and because the fun (at least, apparently) has no way to know that it should access the implicit conversion from string to BsonString for the second value in each pair...

let bdoc = [("a","1");("b","2");] |> Seq.fold (fun (doc:BsonDocument) pair -> doc.Add(fst pair, new BsonString(snd pair))) (new BsonDocument())

... no matter, I thought, I'll use the bigger overload of the constructor

BsonDocument(IDictionary<string, object>)

but this is forced to the following:

let bdoc = (new BsonDocument(dict
  [("a", "1" :> Object); 
   ("b", "2" :> Object);
  ]))

If I take out the upcasts

:> Object

then F# complains that it can't find an overload of BsonDocument.

(long stroll in the garden is over ...)

After all that, the micro question is why, in F#, can it not figure out that the "1" and "2" in the input dictionary are objects and thus find the appropriate overload?

And the bigger-picture macro question is whether I've just missed the appropriate, best-practices, supercool, succinct way to do this in F# ?

Was it helpful?

Solution

This is not a MongoDB issue. The issue is that ("1", "2") is a string * string, so you're creating an IDictionary<string,string> with your dict constructor. F# has inferred the types you've given it. Its type inference won't determine you meant obj in this case. Therefore you have to tell it.

> dict
  [("a", "1" :> obj); 
   ("b", "2" :> obj);
  ];;
val it : System.Collections.Generic.IDictionary<string,obj> =
  seq [[a, 1] {Key = "a";
               Value = "1";}; [b, 2] {Key = "b";
                                      Value = "2";}]
> dict
  [("a", "1"); 
   ("b", "2");
  ];;
val it : System.Collections.Generic.IDictionary<string,string> =
  seq [[a, 1] {Key = "a";
               Value = "1";}; [b, 2] {Key = "b";
                                      Value = "2";}]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top