No Result using Object in Scala - scala

I am trying to print the value of 1, 2 or 3
using this code
but it just says
defined object Demo
import scala.collection.immutable._
object Demo {
val mymap : Map[String, String] =
Map("1" -> "ME", "2" -> "SUPERMAN", "3" -> "ZOD")
def main(args: Array[String]) {
println(mymap("1"));
}
}

Judging from the tags you used (databricks), it looks like you are using either a REPL or some other online worksheet. As such, you don't have to create an object with a main method, but just write in your code, as follows:
import scala.collection.immutable._
val mymap: Map[String, String] =
Map("1" -> "ME", "2" -> "SUPERMAN", "3" -> "ZOD")
println(mymap("1"))
You can play around with this code with another Scala online worksheet, called Scastie (here).
Alternatively, you can also call Demo.main directly from outside of the object definition, passing Array.empty as its only argument.
A few of further notes:
I'm not 100% whether this is true for your environment, but in general importing scala.collection.immutable._ is not strictly necessary
in Scala you can make use of type inference and skip the Map[String, String] type annotation: the compiler will infer it for you
semi-colons are optional in Scala

Related

Why is map.get with 2 parameters possible?

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", "%"))
^

scala map in a method argument can not add key-value

In Scala , how to pass a map to a method as a reference object so that I can add key-value to this map. I tried this code, it doesn't work.
var rtnMap = Map[Int, String]()
def getUserInputs(rtnMap: Map[Int, String]) {
rtnMap += (1-> "ss") //wrong here
}
I understand, by default, argument in a method is val, like final in java, it can provide some safety. but at least it should allow us to insert a new entry. do you have any idea ?
Welcome to functional programming
First of all your use case is possible with mutable map. You have using immutable map because that is by default available in Scala. Everything from the package scala.Predef is by default available in Scala and you don't need to import it by default.
Below code works as excepted.
import scala.collection.mutable.Map
val gMap = Map[Int, String]()
def getUserInputs(lMap: Map[Int, String]) = {
lMap += (1-> "ss")
}
Below call will change the contents of the gMap
getUserInputs(gMap)
Here is the proof
scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
scala>
| val gMap = Map[Int, String]()
gMap: scala.collection.mutable.Map[Int,String] = Map()
scala>
| def getUserInputs(lMap: Map[Int, String]) = {
| lMap += (1-> "ss")
| }
getUserInputs: (lMap: scala.collection.mutable.Map[Int,String])scala.collection.mutable.Map[Int,String]
scala> getUserInputs(gMap)
res2: scala.collection.mutable.Map[Int,String] = Map(1 -> ss)
scala> gMap
res3: scala.collection.mutable.Map[Int,String] = Map(1 -> ss)
In the last Scala repl notice the contents of the gMap. gMap contains the added item.
General code improvements
Do not use mutable collections unless you have a strong reason for using it.
In case of immutable collections new instance is returned when a operation to change the existing datastructure is done. This way the existing data structure does not change. This is good in many ways. This ensures program correctness and also ensures something called as referential transparency (read about it).
so your program should be ideally like this
val gMap = Map.empty[String, String] //Map[String, String]()
def getUserInputs(lMap: Map[Int, String]) = {
lMap += (1-> "ss")
}
val newMap = getUserInputs(gMap)
Contents are added to newMap and the old Map is not changed, stays intact. This is very useful because the code holding on to the gMap and accessing the gMap need not be worried about the changes happening to the underlying structure.(Lets say in multi-threaded scenarios, its very useful.)
Keeping the original structure intact and creating the new instance for changed state is the general way of dealing with state in functional programming. So its important to understand this and practice this.
Deprecated syntax and its removed in Scala 2.12
You declared your function like below
def getUserInputs(lMap: Map[Int, String]) { // no = here
lMap += (1-> "ss")
}
In the above function definition there is no = after the closed parenthesis. This is deprecated in Scala 2.12. So don't use it because Scala compiler gives misleading compilation errors with this function declaration syntax.
Correct way is this.
def getUserInputs(lMap: Map[Int, String]) = {
lMap += (1-> "ss")
}
Notice there is = in this syntax.
To pass a reference to a map and change that map, you'd need to use a mutable Map implementation (the default in Scala is an immutable map, regardless of whether it is declared as a val or a var), so an alternative to #Esardes's answer (one that would also work if rtnMap is not in-scope where getUserInputs is defined) would be:
import scala.collection.mutable
def getUserInputs(map: mutable.Map[Int, String]) {
map += (1 -> "ss") // mutating "map"
}
val rtnMap = mutable.Map[Int, String]() // notice this can be a val
getUserInputs(rtnMap)
println(rtnMap)
// Map(1 -> ss)
You either wrap your value in an object that you then pass as a parameter, or, if it's available in the scope of your function, you can directly write
def getUserInputs = {
rtnMap += (1-> "ss")
}

Scala extend Map (or HashMap) and allow a constructor list of mappings

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.

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}