Why is map.get with 2 parameters possible? - scala

I have 2 maps, map1 and map2, both of type Map[String, MyType].
What I wanted to do was, searching for a key in the first map, if not found, search in the second one, and if still not found, use a default value:
map1.getOrElse(name, map2.getOrElse(name, defVal))
However, what I accidently wrote was:
map1.getOrElse(name, map2.get(name, defVal)).
Surprisingly, this didn't cause a compile-time error (and returned null), although i called get with 2 parameters.
As I can see in the IDE (Eclipse), it calls get(x$1 : Any) : MyType of java.util.Map, instead of get(key : String) : Option[MyType] of scala.collection.MapLike
Why doesn't it report an error, when I call map2.get with a second parameter? As far as I can see in the documentation, map.get only takes one parameter? Does it interpret the two parameters as a tuple or something similar (and use this tuple as Any-parameter)?
If relevant, MyType is a class from a referenced Java-project.
I am quiet new in Scala, so if this is trivial or a basic concept that I missed (and everyone programming scala should already know), please tell me what to search for.
EDIT:
As I saw in the comments, that the problem is not reproduceable, I saw, that I hava import scala.collection.javaConversions._, because I get the collections from a Java-Project that I reference. Now it is also reproducable with a short code like this:
import scala.collection.JavaConversions._
object Main {
def main (args : Array[String]) : Unit = {
val map1 = Map("1" -> 2)
val map2 = Map("2" -> 1)
map1.getOrElse("1", map2.get("2", 0))
}
}

Might be argument adaptation from map2.get(name, defVal) to map2.get((name, defVal)).
Edit: Use JavaConverters. JavaConversions is deprecated.

Well... I am not sure how you made this work, it should not be possible.
It is still not doable ( at least with Scala 2.12.1)
scala> val map1 = Map("1" -> "!", "2" -> "#")
map1: scala.collection.immutable.Map[String,String] = Map(1 -> !, 2 -> #)
scala> val map2 = Map("3" -> "#", "4" -> "$")
map2: scala.collection.immutable.Map[String,String] = Map(3 -> #, 4 -> $)
scala> val s = map1.getOrElse("3", map2.get("3", "%"))
<console>:13: error: too many arguments (2) for method get: (key: String)Option[String]
val s = map1.getOrElse("3", map2.get("3", "%"))
^

Related

What do -> and ! mean in Scala

I am reading some Scala code. What does the -> mean in the following context?
var queries = { "Select apple from farm" -> None, "Select orange from fram" -> None, "Select blueberry from anotherFarm" -> Some( #randomStuff ) }
It looks like a list of lambda functions but I thought it should be => in that case instead of ->.
Also,
what does this single line code mean?
def onConnection(id) = { application ! turnOnApplication(id) }
Specifically, I am confused with the use of !. It doesn't seem to be a "NOT" as it is in most languages
The -> symbol is one way to define a tuple in Scala. The below are all equivalent:
val apples1 = "Select apple from farm" -> None
val apples2 = ("Select apple from farm" -> None)
val apples3 = ("Select apple from farm", None)
As for the !:
def onConnection(id) = { application ! turnOnApplication(id) }
! in Scala can be the negation operator, but ! in the above code snippet looks like tell from Akka (Akka is the main actor library for Scala). This pattern is used to send a message to an actor. So if application is a reference to an actor, the code snippet sends the result of turnOnApplication(id) to the application actor. From the linked documentation:
"!" means “fire-and-forget”, e.g. send a message asynchronously and return immediately. Also known as tell.
The thin arrow -> is Tuple syntax. It's just a different way of writing Tuples. I.e.
val x: (Int, String) = 3 -> "abc"
Is the same as writing:
val x: (Int, String) = (3, "abc")
The arrow syntax is done by providing an implicit class ArrowAssoc which defines a method def ->[B](y: B): (A, B). ArrowAssoc is part of Predef which is inserted into every Scala source file. You can find the docs here.
The bracket syntax meanwhile is syntactic sugar done by the compiler.
You can form tuple using two syntaxes
1) Using comma
val tuple = (1, 2)
2) Using -> (arrow)
val tuple = 1 -> 2
Scala repl
scala> val tuple = (1, 2)
tuple: (Int, Int) = (1,2)
scala> val tuple = 1 -> 2
tuple: (Int, Int) = (1,2)
Finding-symbols defines -> as Method provided by implicit conversion. Just look at the methods tagged with implicit that receive, as parameter, an object of type that is receiving the method. For example:
"a" -> 1 // Look for an implicit from String, AnyRef, Any or type parameter
In the above case, -> is defined in the class ArrowAssoc through the method any2ArrowAssoc that takes an object of type A, where A is an unbounded type parameter to the same method.
tutorialPoint definde ! as It is called Logical NOT Operator. Use to reverses the logical state of its operand. If a condition is true then Logical NOT operator will make false.

Is '->' a operator? What does it return?

In Scala I can build a Map this way:
val map = Map(1 -> "one", 2 -> "two", 3 -> "three")
But what does it do? The arguments should be evaluated, so 1 -> "one" has a value. What's it?
I also noticed this expression returns true:
scala> 1 -> "one" == (1, "one")
res1: Boolean = true
So what's the difference?
It comes from the class ArrowAssoc (http://www.scala-lang.org/api/current/scala/Predef$$ArrowAssoc.html). Look in the object Predef (http://www.scala-lang.org/api/current/scala/Predef$.html) which contains all the pre-imported methods. There you should find the method any2ArrowAssoc[A](x: A): ArrowAssoc[A] which is the implicit conversion allowing the method -> to be called on anything.
To elaborate on this answer, this means that the following code is implicit in your examples:
scala> 'a' -> 1 == (any2ArrowAssoc('a').->(1))
res0: Boolean = true
In short it's just an extension method on Pair (or Tuple2) type. If you take a look at Map type in scala, you'' see that it consist of pairs or tuples of arity of 2. As i know it was defined specially for Map construction to denote key value association key -> value, but you can easily write Map(("a", 1), ("b", 2)) which is equivalent to Map("a" -> 1, "b"-> 2), just looks better. So 1 -> "one" == (1, "one") is equals cause it's the same. In scala, -> is defined as an implicit class in Predef object
From book "Programming in Scala" (http://booksites.artima.com/programming_in_scala_2ed):
The Scala compiler transforms a binary operation expression like 1 -> "Go to island." into (1).->("Go to island.").
Thus, when you say 1 -> "Go to island.", you are actually calling a method named -> on an integer with the value 1, passing in a string with the value "Go to island."
This -> method, which you can invoke on any object in a Scala program, returns a two-element tuple containing the key and value.
The Scala mechanism that allows you to invoke -> on any object, implicit conversion.

Option[Map[String, String]] can get a value weirdly

I found Option[Map[String, String]] works weirdly like this:
scala> val fileInfo: Option[Map[String, String]] = Some(Map( "type" -> "hoge" ))
fileInfo: Option[Map[String,String]] = Some(Map(type -> hoge))
scala> fileInfo.get("type")
res1: String = hoge
I think the Option "get" method doesn't take any argument, so this is super weird for me. Why does it work? Is it an implicit conversion or a bug? I want to make sure how it works.
My specifications are:
Scala version 2.9.2
Java 1.6.0_43
This desugars to fileInfo.get.apply("type"), i.e. you are not really passing a parameter to Option#get, but calling .apply("type") on the result.
Scala allows you to omit braces in some cases and you've been a victim of ambiguity that this feature created: what you've done is simply unwrapped Option (and get underlying Map) -- Option has not only widely used .getOrElse, but also unsafe .get method which fails with exception when there is None:
val fileInfo: Option[Map[String, String]] = Some(Map( "type" -> "hoge" ))
val map = fileInfo.get
// map: Map[String,String] = Map(type -> hoge)
// now actual map lookup
map("type")
// res0: String = hoge
// but this obviously won't work
val throwy: Option[Map[String, String]] = Option(null) // None
throwy.get("type")
// java.util.NoSuchElementException: None.get
fileInfo.get("type")
is translated to:
fileInfo.get().apply("type")
So you unwrap the option and then get an element on the Map. (apply gets an element of the map non-optional, i.e. fails if the key doesn't exist).

Type alias for immutable collections

What is the best way to resolve the compilation error in the example below? Assume that 'm' must be of type GenMap and I do not have control over the arguments of myFun.
import scala.collection.GenMap
object Test {
def myFun(m: Map[Int, String]) = m
val m: GenMap[Int, String] = Map(1 -> "One", 2 -> "two")
//Build error here on m.seq
// Found scala.collection.Map[Int, String]
// Required scala.collection.immutable.Map[Int, String]
val result = myFun(m.seq)
}
EDIT:
I should have been clearer. In my actual use-case I don't have control over myFun, so I have to pass it a Map. The 'm' also arises from another scala component as a GenMap. I need to convert one to another, but there appears to be a conflict between collection.Map and collection.immutable.Map
m.seq.toMap will solve your problem.
According to the signature presented in the API toMap returns a scala.collection.immutable.Map which is said to be required in your error message. scala.collection.Map returned by the seq method is a more general trait which besides being a parent to immutable map is also a parent to the mutable and concurrent map.

Object is not a value error in scala

While trying to make a map in Scala, I receive the following error message: object Map is not a value
The code I'm using is the following:
val myStringMap = Map[String, String]("first" -> "A", "second" -> "B", "third" -> "C")
I am quite puzzled as to why I cannot create this map because after looking at Scala documentation, it seems to me that the syntax of my code is proper.
When you see the error "object is not a value" this typically means that the in-scope type is a Java type - you probably are importing java.util.Map in scope
scala> Map(1 -> "one")
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one)
But
scala> import java.util.Map
import java.util.Map
scala> Map(1 -> "one")
<console>:9: error: object Map is not a value
Map(1 -> "one")
^
Remember, in scala each class comes with a (optional) companion object which is a value. This is not true of Java classes.
Because in scala each native scala class comes with an (optional) companion object (allowing for assignment from the companion object as in your example code) when incorporating a java class into scala code always remember to instantiate the class by invoking the constructor, ie. use keyword "new", thus creating a value.
Just found this so maybe it will be useful to share my solution.
If you have imported java.util.Map and need to use scala.collection.immutable.Map then use it with the full name so instead of
Map(1 -> "one")
do
scala.collection.immutable.Map(1 -> "one")
This way it will know what you mean
I got similar error on Lists. try this in scala console:
import java.util.List object test { def a():List[String] = { val list = List[String](); null }}
you'll get the err "Object List is not a value."
You get it because you're hiding the built-in List type, that is because List is different from java.util.List
What if someone wants to use util.List?
you can use a qualified name or a rename import
! import java.util.{List => JList}
import java.util.{List=>JList}