tags:

views:

160

answers:

3

Hello

I am getting a weird error when trying to use a Java map in Scala. This is the snippet of code

val value:Double = map.get(name)
  if (value eq null) map.put(name, time) else map.put(name, value + time)

the map is defined as

val map=new ConcurrentHashMap[String,Double]

and this is the error I am getting

error: type mismatch;
found   : Double
required: ?{val eq: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method double2Double in object Predef of type (Double)java.lang.Double
and method doubleWrapper in object Predef of type (Double)scala.runtime.RichDouble
are possible conversion functions from Double to ?{val eq: ?}
if (value eq null) map.put(name, time)

I am new to Scala so I am having a hard time parsing the stacktrace. Any help would be appreciated

+5  A: 

First, map.get(name) will not return null in the case when name key is not present in the map. It will instead return a Double(0.0).

Second, the error that you see is because scala is trying to implicitly convert the returned Double value to a type suitable for the eq comparision and it finds more than one implicit conversions in the scope.

The better way to do what you are doing is

if (map contains name) map.put(name, map.get(name) + time) 
else map.put(name, time)
abhin4v
Thank you for the explanation
Mansoor Ashraf
+3  A: 
error: type mismatch;
found   : Double
required: ?{val eq: ?}

The problem here is that eq is only defined for classes extending AnyRef and extended by Null, but Double extends AnyVal instead.

Daniel
+1  A: 

As Daniel wrote, part of the problem trying to write in a Java style with Double comes from the fact that Double in Scala is scala.Double and not java.lang.Double. If you wanted to program in Java style you would have to go along the following lines:

//
// a) Java style, with concurrency problem
//
import java.lang.{Double=>JDouble}
import java.util.concurrent.ConcurrentHashMap
val map = new ConcurrentHashMap[String, JDouble]
def update(name: String, time: Double) {
  val value: JDouble = map.get(name)
  if (value eq null)
    map.put(name, time)
  else
    map.put(name, JDouble.valueOf(value.doubleValue + time))
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map.get("foo") == 83.0d)

Scala 2.8 contains a Scala wrapper for ConcurrentMaps, so you can easily avoid the problem of java.lang.Double vs scala.Double. I'll keep the program structure for a moment.

//
// b) Scala style, with concurrency problem.
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()
def update(name: String, time: Double) {
  map.get(name) match {
    case None => map.put(name, time)
    case Some(value) => map.put(name, value + time)
  }
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)

In variant b) above there is neither the problem of representing the missing value as 0.0d nor the problem that java.lang.Double doesn't play nicely with operators and boxing. But both versions a) and b) are questionable regarding their concurrency behaviour. Mansoor's code uses a ConcurrentHashMap, which has the purpose of allowing for concurrent access to the map. In the original version of the code there is a chance that an update to the map is lost between getting the old value and storing value + time. Variant c) below tries to avoid that problem.

//
// c) Scala style, hopefully safe for concurrent use ;)
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()

def update(name: String, time: Double) {
  val valueOption: Option[Double] = map.putIfAbsent(name, time)
  def replace(value: Double) {
    val replaced = map.replace(name, value, value + time)
    if (!replaced) {
      replace(map(name))
    }
  }
  valueOption foreach { replace(_) }
}

update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)
mkneissl