I'm trying how to figure out and work with maps with enums as keys in Scala. Looking at this question, I can instantiate maps, but when I try to update the map in place, I get a type mismatch error. What is going on here?
object MyEnums extends Enumeration {
type MyEnum = Value
val BOB, TED, JEN = Value
}
var mymap = scala.collection.mutable.Map[MyEnums.Value, Long]()
mymap += (MyEnums.JEN -> 100L)
throws:
<console>:38: error: type mismatch;
found : (MyEnums.Value, Long)
required: (MyEnums.Value, Long)
mymap += (MyEnums.JEN -> 100L)
If I do the same thing, but use e.g. strings as the key type, this works as expected.
EDIT: These issues occur when using scala in spark-shell, not the normal scala repl.
Related
I am trying to parse the input data to case classes inside a TreeSet but it doesn't seem to work.
case class Block(letter: Char)
// This does not compile
val brokenBlocks: collection.immutable.TreeSet[Block] = "A B C".split(' ').map(letter => Block(letter(0)))(collection.breakOut)
// Although this compiles
val workingBlocks: collection.immutable.TreeSet[Int] = "A B C".split(' ').map(letter => letter(0).asDigit)(collection.breakOut)
Compilation error:
type mismatch; found : scala.collection.generic.CanBuildFrom[Any,Char,String]
required: scala.collection.generic.CanBuildFrom[Array[String],Block,scala.collection.immutable.TreeSet[Block]]
I am trying this in scala worksheet.
No relation with case classes at all. If you had tried to build a List or a Vector it would have worked fine.
A TreeSet requires that there is an Ordering on its element's type. There is one on Int, but none on Block.
If you add one, say in the companion object
object Block {
implicit val ordering: Ordering[Block] = Ordering.by(_.letter)
}
then it works.
Converting from java.util.ArrayList to scala.collection.immutable.List, the 2.10 compiler and run-time may seem to be in disagreement, about the type of val emits:
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
//return(emits)
return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields:
converted from class java.util.ArrayList to class scala.collection.immutable.$colon$colon
Now changing to return the real value by changing only the return line -
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
return(emits)
//return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields a compilation error:
[error] reproduce.scala:23: type mismatch;
[error] found : Iterable[String]
[error] required: List[String]
[error] return(emits)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
What would be the simple explanation for this?
How would I better approach the conversion?
JavaConverters convert to the Scala collection closest to the Java collection that it has pimped. You still have to call toList to further transform it to the collection you want:
val emits = emitsJ.asScala.toList map (i => i.getKeyword)
See related: What is the difference between JavaConverters and JavaConversions in Scala?
So what's happening here is that the underlying object returned to you from asScala is a List but it's been downcast to an Iterable since List <: Iterable this is all fine until you want to use the Iterable as a list. The easiest option is to call toList on it.
For a little more detail you can browse the source:
case class JCollectionWrapper[A](underlying: ju.Collection[A]) extends AbstractIterable[A] with Iterable[A] {
def iterator = underlying.iterator
override def size = underlying.size
override def isEmpty = underlying.isEmpty
def newBuilder[B] = new mutable.ArrayBuffer[B]
}
So your call to asScala gives you back this wrapper. Next, your call to map is using this CanBuildFrom which is then used in this map operation, which finally gives you the result which happens to be a List because the builder is a ListBuffer and the result is a List, which again happens to be an Iterable because the can build from pattern downcasts to Iterable. Hope that explains everything :)
the return type of trie.parseText is declared as java.util.Collection[Emit]. This isn't very specific, there are lots of possible subtypes of Collection, they aren't specifying what specific type they are going to return, it could be a TreeSet, it could be a Vector, it could be an ArrayList. But as far as the compiler is concerned, it could be anything which is a sub-type of Collection. You have inspected it at run-time and seen that for some particular input, it happened to return an ArrayList, but there is no way the compiler could know this.
When you call .asScala on this, you are using this implicit definition from JavaConverters
implicit def iterableAsScalaIterableConverter[A](i: java.lang.Iterable[A]): convert.Decorators.AsScala[Iterable[A]]
Which converts a java.lang.Itaerable into a scala.collection.Iterable.
There is no converter for Collection so you get a converter for the next most specific thing, Iterable. you call map on this Iterable and get an Iterable back.
Now, just like you have inspected the runtime value of the Collection returned from parseText and seen that it was a ArrayList you have inspected the value returned from this map operation and have seen that it is a scala.collection.immutable.List, but again, the compiler can't know this, all that it can know is that you gotten something which is a subclass of Iterable back.
Simply calling .toList on the resulting Iterable should be all you need to do.
I asked a longer question, but it seems it's too much code for people to sort through so I've created this question to focus on one smaller, specific problem I'm facing regarding use of macros in Scala.
Consider the following code snippet:
val tpe = weakTypeOf[T]
val companion = tpe.typeSymbol.companionSymbol
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val toMapParams = fields.map { field =>
val name = field.name
val decoded = name.decoded
q"$decoded -> t.$name"
}
Note that fields is just the list of parameters for the primary constructor of a case class in this code. Where I'm confused is the result of the quasiquote q"$decoded -> t.$name". What does this mean exactly? And what type should it have? I'm getting a compile error stating the following:
Multiple markers at this line
- Implicit conversions found: q"$decoded -> t.$name" => Quasiquote(q"$decoded -> t.
$name")
- type mismatch; found : field.NameType required: c.universe.TermName
- type mismatch; found : field.NameType required: c.universe.TermName
Can anyone explain this error? Thanks.
The type of fields is List[Symbol], which means that the type of names of those fields is inconclusive (unknown whether it's a TermName or TypeName). This means that you can't insert such names essentially anywhere in a quasiquote.
A simple fix would be to do val name = field.name.toTermName, explicitly telling the compiler that it's looking at a term name, so that quasiquote knows how to process it.
I have the following definition of an enum:
object GraphType extends Enumeration {
type Type = Value
val MESSAGE, REQUEST, ERRORS = Value
}
Now I am trying to map each of the type to the corresponding, new TimeSeries as follows:
val dataSets = ( GraphType.values map (graphType => graphType -> new TimeSeries(graphType)) ).toMap
the type system lists datasets as Map[GraphType.Value, TimeSeries] which is precisely what I want. However, compilation fails with error message:
error: diverging implicit expansion for type scala.collection.generic.CanBuildFrom[ird.replay.gui.GraphType.ValueSet,(ird.replay.gui.GraphType.Value, org.jfree.data.time.TimeSeries),That]
starting with method newCanBuildFrom in object SortedSet
val dataSets = GraphType.values map (graphType => graphType -> new TimeSeries(graphType)) toMap
COuld anyone provide some explanation for this, rather cryptic, error message? Thanks
Try converting the Set of values for the enum to a List first like so:
val dataSets = (GraphType.values.toList.map(gt => (gt, new TimeSeries(gt)))).toMap
Something about that being a Set did not agree with how you were attempting to convert it to a Map, but it seems to work just fine with a List
I'm trying to port my application to Scala 2.10.0-M2. I'm seeing some nice improvements with better warnings from compiler. But I also got bunch of errors, all related to me mapping from Enumeration.values.
I'll give you a simple example. I'd like to have an enumeration and then pre-create bunch of objects and build a map that uses enumeration values as keys and then some matching objects as values. For example:
object Phrase extends Enumeration {
type Phrase = Value
val PHRASE1 = Value("My phrase 1")
val PHRASE2 = Value("My phrase 2")
}
class Entity(text:String)
object Test {
val myMapWithPhrases = Phrase.values.map(p => (p -> new Entity(p.toString))).toMap
}
Now this used to work just fine on Scala 2.8 and 2.9. But 2.10.0-M2 gives me following warning:
[ERROR] common/Test.scala:21: error: diverging implicit expansion for type scala.collection.generic.CanBuildFrom[common.Phrase.ValueSet,(common.Phrase.Value, common.Entity),That]
[INFO] starting with method newCanBuildFrom in object SortedSet
[INFO] val myMapWithPhrases = Phrase.values.map(p => (p -> new Entity(p.toString))).toMap
^
What's causing this and how do you fix it?
It's basically a type mismatch error. You can work around it by first converting is to a list:
scala> Phrase.values.toList.map(p => (p, new Entity(p.toString))).toMap
res15: scala.collection.immutable.Map[Phrase.Value,Entity] = Map(My phrase 1 -> Entity#d0e999, My phrase 2 -> Entity#1987acd)
For more information, see the answers to What's a “diverging implicit expansion” scalac message mean? and What is a diverging implicit expansion error?
As you can see from your error, the ValueSet that holds the enums became a SortedSet at some point. It wants to produce a SortedSet on map, but can't sort on your Entity.
Something like this works with case class Entity:
implicit object orderingOfEntity extends Ordering[Entity] {
def compare(e1: Entity, e2: Entity) = e1.text compare e2.text
}