What is the best way to create and pass dictionaries containing multiple types in scala?

In the dictionary, I mean a lightweight map from names to values ​​that can be used as the return value of a method.

The options I know about include creating case classes, creating anon objects, and creating maps from Strings -> Any.

  • Case classes require mental overhead to create (names), but they are strongly typed.
  • Anon objects do not seem to be documented, and it is not clear to me how to use them as arguments, since there is no named type.
  • Maps from String -> Any require casting to retrieve.

Is there anything better?

Ideally, they can be built from json and converted back to it when necessary.

I do not need static typing (although it would be nice, I see how it would be impossible), but I want to avoid an explicit cast.

+2
source share
4 answers

Here is the main problem with what you want:

def get(key: String): Option[T] = ... val r = map.get("key") 

Type r will be determined from the return type get - so what should this type be? How can this be determined? If you make this a type parameter, then this is relatively easy:

 import scala.collection.mutable.{Map => MMap} val map: MMap[String, (Manifest[_], Any) = MMap.empty def get[T : Manifest](key: String): Option[T] = map.get(key).filter(_._1 <:< manifest[T]).map(_._2.asInstanceOf[T]) def put[T : Manifest](key: String, obj: T) = map(key) = manifest[T] -> obj 

Example:

 scala> put("abc", 2) scala> put("def", true) scala> get[Boolean]("abc") res2: Option[Boolean] = None scala> get[Int]("abc") res3: Option[Int] = Some(2) 

The problem, of course, is that you have to tell the compiler which type you want to save on the map under this key. Unfortunately, this does not happen: the compiler cannot know what type will be stored under this key during compilation.

Any decision you take will end with the same question: anyway, you need to tell the compiler which type should be returned.

Now this should not be a burden in the Scala program. Take this r above ... then you will use this r for something, right? What you use for this will have methods that correspond to some type, and since you know what methods are, then you should also know what should be of type r .

If this is not so, then there is something fundamentally wrong in the code - or maybe you didn’t switch from the fact that the card did not know what you would do with it.

+3
source

So, you want to parse json and turn it into objects that look like javascript objets described in json input? If you want static typing, the case classes are pretty much your only option, and there are already libraries that handle this, such as lift-json.

Another option is to use Scala 2.9 experimental support for dynamic typing. This will give you an elegant syntax due to type safety.

+2
source

You can use the approach that I saw in the casbah library when you explicitly pass the type parameter to the get method and throw the actual value inside the get method. Here is a quick example:

 case class MultiTypeDictionary(m: Map[String, Any]) { def getAs[T <: Any](k: String)(implicit mf: Manifest[T]): T = cast(m.get(k).getOrElse {throw new IllegalArgumentException})(mf) 
private def cast[T <: Any : Manifest](a: Any): T = a.asInstanceOf[T] }
implicit def map2multiTypeDictionary(m: Map[String, Any]) = MultiTypeDictionary(m)
val dict: MultiTypeDictionary = Map("1" -> 1, "2" -> 2.0, "3" -> "3")
val a: Int = dict.getAs("1") val b: Int = dict.getAs("2") //ClassCastException val b: Int = dict.getAs("4") //IllegalArgumetExcepton

You should note that there are no real compile-time checks, so you have to deal with all the exception exceptions.

UPD Working Class MultiTypeDictionary

+2
source

If you have only a limited number of types that can occur as values, you can use some type of union (type aka disjoint), for example, a Map[Foo, Bar | Baz | Buz | Blargh] Map[Foo, Bar | Baz | Buz | Blargh] Map[Foo, Bar | Baz | Buz | Blargh] . If you have only two possibilities, you can use Either[A,B] , giving you Map[Foo, Either[Bar, Baz]] . For the three types, you can trick and use Map[Foo, Either[Bar, Either[Baz,Buz]]] , but this syntax is obviously not scalable enough. If you have more types, you can use things like ...

+1
source

All Articles