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# ?
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";}]
Related
In scala exercies I have found the following example:
val set = Set(4, 6, 7, 8, 9, 13, 14)
val result = set.toIterable
with the following description:
toIterable will convert any Traversable to an Iterable. This is a base trait for all Scala collections that define an iterator method to iterate through the collection's elements
But Set is already an Iterable, so what's the point of this method? If this isn't the valid case, could you point me one?
In Scala 2.13 there is no more Traversable:
Simpler type hierarchy
No more Traversable and TraversableOnce. They remain only as
deprecated aliases for Iterable and IterableOnce.
Calling toIterable on Set is redundant as it will simply return this same collection:
This collection as an Iterable[A]. No new collection will be built if
this is already an Iterable[A].
Examples where toIterable would have an effect would be
"Hello".toIterable
Array(1).toIterable
which implicitly converts to
wrapString("Hello").toIterable
wrapIntArray(Array(1)).toIterable
and make these Java-like types into Scala collections proper.
In addition to Mario Galic's answer, the other thing it does is change the static type. If you and the compiler knew it was a Set before the call, you don't know afterwards. Though the same can be achieved with a type ascription
val result: Iterable[Int] = set
(and this will work for strings and arrays as well), then you need to write out the type parameter, which may much more complex than Int.
Why would I use it? If i know it's a Set, why would I change the type to Iterable?
it can be in a method which can be overridden and doesn't have to return Set in subclasses:
class Super {
def someValues = {
val set = ... // you want to avoid duplicates
set
}
}
class Sub : Super {
override def someValues = {
List(...) // happens to have duplicates this time
}
doesn't compile, but would if Super#someValues returned set.toIterable (though it's generally good practice to have explicit return types).
It can influence later inferred types:
val arr = Array(set)
arr(0) = List(0, 1, 2, 3)
doesn't compile, but would with Array(set.toIterable).
I have a Scala code:
import collection.mutable._
def myMethod(mycollection: Map[A, B]) = {
...
}
How do I call this method?
Tried this:
myMethod(["test1", "test2"])
Got error:
Identifier expected but 'def' found
Thanks.
A Map is a data structure that maps a key (of some type K) to a value (of some type V). In Scala, such a pair can be denoted by the syntax key -> value. If your intent is to have a single String key "test1" that maps to a String value of "test2", then you can do that as follows:
Map("test1" -> "test2")
Your declaration of myMethod is invalid: you need to either define actual types for A and B or make them generic parameters for your method (so that the method is generic):
// With specific types (assuming both keys and values have String types):
def myMethod(mycollection: Map[String, String]) = //...
// In generic form (allows any key type A, or value type B):
def myMethod[A, B](mycollection: Map[A, B]) = //...
Either way, you can then use the result as the argument in a call to your method as follows:
myMethod(Map("test1" -> "test2"))
Some points to note:
Square brackets are used when defining generic type parameters, or specifying the types used as type parameters.
Type parameters can be inferred from the values supplied. For example Map("test1" -> "test2") uses String as the type for both the key and the value, and is equivalent to Map[String, String]("test1" -> "test2").
If you need more than one key/value pair, list them with a comma separator, for example: Map("key1" -> "value1", "key2" -> "value2", "key3" -> "value3")
I strongly recommend that you read a good book on Scala, such as the excellent Programming in Scala, 3rd Edition by Odersky, Spoon & Venners, in order to become familiar with its syntax and standard library.
As a final point, I would strongly recommend that you use the immutable version of Map whenever possible. If you're not familiar with functional programming principles, this will seem unusual at first, but the benefits are huge.
I'm writing a small application to familiarize myself with the FRP paradigm and RxJava.
I have two methods; getUsers() and getNextTask(userId). These methods return Observable<User> and Observable<Task> respectively and getNextTask depends on User items emitted from getUsers Observable.
I have written the following code which combines the results of the two calls into one object, UserSummary, using flatMap():
getUsers()
.flatMap(u -> (Observable<UserSummary>) getNextTask(u.getId()).map(t -> new UserSummary(u, t))
.subscribe(System.out::println);
This code works fine and emits the values that I expect, however, I expected values of type UserSummary and instead received values of type Object which I then cast to UserSummary.
My question is, is flatMap() a good place to call getNextTask() or is there perhaps a more effective method of calling methods with dependencies?
Edit
With regards to the flatMap returning Observable<Object> instead of Observable<UserSummary> as I would have expected of a generic method, the following quick snippet illustrates the problem. The flatMap operator is expected to return Observable<String>, however it returns Observable<Object>. Therefore, returns have to be cast to their respective types.
Integer[] numberArray = {1, 2, 3, 4, 5};
Observable.from(numberArray)
.flatMap(i -> {
String[] letterArray = {"a", "b", "c", "d", "e"};
return Observable.from(letterArray)
.map(x -> x + i);
}).subscribe(System.out::println);
I am very new to Scala, I try some tutorials but I didn't get the point in this problem here:
val reverse = new mutable.HashMap[String, String]() with mutable.SynchronizedMap[String, String]
def search(query: String) = Future.value {
val tokens = query.split(" ")
val hits = tokens map { token => reverse.getOrElse(token, Set()) }
if (hits.isEmpty)
Nil
else
hits reduceLeft {_ & _} toList // value & is not a member of java.lang.Object
}
The compiler says value & is not a member of java.lang.Object. Can somebody explain me why I am getting a compiler error ? I have this from this tutorial here: https://twitter.github.io/scala_school/searchbird.html
"tokens" is of type Array[String]. Now, when you iterate over the array, there are two possibilities. Either reverse will have a value for the token or not. If it has, then the Array element get a string value otherwise an empty set.
For example: Lets say reverse has two values - ("a" -> "a1", "b" ->"b1") a maps to a1 and b maps to b1.
Suppose, The query string is "a c".
tokens will be ["a","c"] after splitting.
After mapping you will get in array ["a1", Set()] (a got mapped to a1 and there is no value for "c" in the map hence, you got an empty Set())
Now, the overall type of the hits array is Array[Object].
So, now you are getting an error as the last line will be "&" operator on 2 Objects according to the compiler.
Mohit Has The right answer, you end up with an Array of objects. This is because your HashMap for reverse has a value type of String, so it'll return a String for a given key. Your getOrElse however, will return a Set type if the key is not found in your HashMap reverse. These need to return the same type so you don't end up with an Array[Objects]
If you notice a few lines above in the tutorial you linked, reverse is defined as follows:
val reverse = new mutable.HashMap[String, Set[String]] with mutable.SynchronizedMap[String, Set[String]]
I have a special Map that will eventually do some consistency checking of values, which are restricted to have special meaning. For now I just want to create a Schema that acts exactly like a Map[String, Any], in particular I'd like to instantiate is with a list of mappings, and not force the types for the Map to be specified, so they are always [String, Any]. So instead of
val myMap:Map[String,Any] = Map("one" -> 1, "two" -> "2", ...)
I'd like to be able to have:
val mySchema:Schema = Schema("one" -> 1, "two" -> "2", ...)
Map is a trait so I think I need to extend a class like HashMap
class Schema extends HashMap[String, Any]
when I instantiate it with a list of initial mappings I get
val mySchema = new Schema("one" -> 1, "two" -> "2", ...)
Error:(110, 19) too many arguments for constructor Schema: ()drivers.Schema
val mySchema = new Schema("one" -> 1, "two" -> "2")
^
There is some magic inside HashMap that is far beyond me to read (it extends 1 class with 5 traits). But it looks like the constructor's "contents" (a list of mappings?) are passed to something's initWithContents(contents) pseudo constructor. Do I need something like that there?
class Schema(elems: Tuple2[String, Any]*) extends HashMap[String, Any] {
this ++= elems
}
val mySchema = new Schema("one" -> 1, "two" -> "2")
Explanation:
-> is syntactic sugar for a tuple, so the constructor type is a variable number of tuples
The "normal" way of constructing a Map/HashMap calls the apply method in the companion object which is implemented in GenMapFactory.scala.
Unfortunately, this does not work for an immutable HashMap. As far as I can tell, the only way to "extend" an immutable HashMap is by creating a class that holds an internal reference to one, as described e.g. in this answer to a similar question on SO.
You'll notice that the syntax you're using to create the Schema instance is slightly different than your target syntax as well as the standard Map syntax:
Schema("one" -> 1, "two" -> "2", ...)
new Schema("one" -> 1, "two" -> "2", ...)
Map("one" -> 1, "two" -> "2", ...)
In particular, there is a new in the second case. When you create a Map without new, you are invoking a function called apply on a companion module of the same name. In this case, the function which accepts a variable length sequence of arguments is defined the Map object's inherited GenMapFactory type and it has this signature:
def apply[A, B](elems: (A, B)*): CC[A, B] = (newBuilder[A, B] ++= elems).result
The magic derives the from Scala compiler resolving this method, because methods in the companion module are part of its implicit scope. You will need to do something similar in by adding a Schema object:
object Schema {
def apply(entries: (String, Any)*) = new Schema() ++ entries
}
If you define the object, this will work:
Schema("one" -> 1, "two" -> "2") // scala.collection.immutable.Map[String,Any](one -> 1, two -> 2)
One downside of this approach is that you lose type specificity after construction, but if you're just performing consistency checking on creation this may be enough. If you want the Schema type to be returned on invocation of the apply function or returned by other methods calls implemented in super types and enclosing scope, you will probably need to integrate with the Scala 2.8 collections API as documented here.