I have a map of type Map[_, Any], and I want to extract the values in their native format (without resorting to .asInstanceOf[_]).
Something like this...
val m: Map[String, Any] = Map("i" -> 1, "s" -> "test")
val i: Option[Int] = m.get("i")
val s: Option[String] = m.get("s")
Obviously that fails.
I don't like this approach, but I was thinking I could do something like this... but even this still comes out as Any instead of Int or String.
trait MyType[A] {
def value: A
}
implicit class MyInt(i: Int) extends MyType[Int] { def value: Int = i }
implicit class MyString(s: String) extends MyType[String] { def value: String = s }
val m: Map[String, MyType[_]] = Map("i" -> 1, "s" -> "test")
val i: Option[Int] = m.get("i").map(_.value)
val s: Option[String] = m.get("s").map(_.value)
Then I thought maybe some wrapper around the Map...
case class MyMap(m: Map[String, Any]) {
def get[A](k: String)(implicit ev: Option[Any] => Option[A]): Option[A] = m.get(k)
}
But that STILL comes out as Any. I just can't figure out how to convert Any => native.
So my questions are...
Why does this fail?
What are better way(s) to get the values out in their native format? Simplest and/or no external dependencies would be ideal... but I'm honestly open to anything (though caveat I'm still on scala 2.11 for now).
Thanks!
You cannot guess the runtime type for the reasons that have already been explained in the comments - this information is not there, once it's Any, all type information is lost, there is nothing you can do about it.
So, you'll have to provide the expected type yourself. How about an .as[T] helper method?
// This code is specifically for 2.11, please don't use it for more recent versions,
// see link below.
val m: Map[String, Any] = Map("i" -> 1, "s" -> "test")
import scala.reflect.{ ClassTag, classTag }
implicit class As(a: Any) {
def as[T](implicit ct: ClassTag[T]): Option[T] = ct.unapply(a)
}
println(m("i").as[Int].map(_ + 41).get)
println(m("s").as[String].map("This is a " + _).get)
This will print
42
This is a test
Brief explanation:
The As wrapper "pimps" all the objects, and attaches an .as method to everything.
The unapply does the checking, the casting, and the wrapping in an Option.
It will not work for generic types like List[Int] vs. List[String] etc, because this information is simply not available at runtime.
EDIT: Thanks #MarioGalic for greatly simplifying the solution.
Related
I am new to Scala and searched for the same as to how can we change from Option[String] to a Map[String,trait] but could not find much .
The thing is I have a field of type Option[String] and I have to pass that value to a case class which takes input as a Map[String,User Defined trait].
That's what I want to know as to how can I convert the value of Option[String] to Map[]
Can we use Option.fold in this?
Could you please guide me further on this.
TIA
Consider using toMap as shown in the following example:
trait UDTrait[A] {
def len(s: A): Int
}
case class MyClass(m: Map[String, UDTrait[String]])
def myOptionToMap(opt: Option[String]): Map[String, UDTrait[String]] =
opt.map((_, new UDTrait[String]{ def len(s: String) = s.length })).toMap
MyClass(myOptionToMap(Some("a")))
// res1: MyClass = MyClass(Map(a -> $anonfun$myOptionToMap$1$$anon$1#10aabada))
MyClass(myOptionToMap(None))
// res2: MyClass = MyClass(Map())
Alternatively, you can use fold, as follows:
def myOptionToMap(opt: Option[String]): Map[String, UDTrait[String]] =
opt.fold(Map.empty[String, UDTrait[String]])(s =>
Map(s -> new UDTrait[String]{ def len(s: String) = s.length })
)
Option[ T ] is a container for zero or one element of a given type. An Option[T] can be either Some[T] or None object, which represents a missing value.
A Map is an Iterable consisting of pairs of keys and values. If you want to construct a map with one key value pair from an option which is non-empty, you can do the following:
trait Foo
def convertNonEmptyOptionToMap(a:Some[String], t: Foo): Map[String, Foo] = Map(a.get -> t)
That said, I don't completely understand what you're trying to do. More context with examples would be helpful.
In a Play application I'm working on, I'm trying to improve our system for processing flags, some of which are meant to be persistent options as a user navigates our app via links. I'd like to use Shapeless to map from a definition of the option to its value, and also to synthesize new query parameter from only the ones marked to be propagated. I also want to be able to take advantage of Shapeless's Record functionality to get strongly typed dereferencing of the parameter values. Unfortunately, I'm not sure if I'm approaching this in a valid way in Shapeless.
The following is one block of code, interrupted by some explanatory comments.
Here are the basic data types I'm working with:
import shapeless._
import poly._
import syntax.singleton._
import record._
type QueryParams = Map[String, Seq[String]]
trait RequestParam[T] {
def value: T
/** Convert value back to a query parameter representation */
def toQueryParams: Seq[(String, String)]
/** Mark this parameter for auto-propagation in new URLs */
def propagate: Boolean
protected def queryStringPresent(qs: String, allParams: QueryParams): Boolean = allParams.get(qs).nonEmpty
}
type RequestParamBuilder[T] = QueryParams => RequestParam[T]
def booleanRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Boolean] = { params =>
new RequestParam[Boolean] {
def propagate: Boolean = willPropagate
def value: Boolean = queryStringPresent(paramName, params)
def toQueryParams: Seq[(String, String)] = Seq(paramName -> "true").filter(_ => value)
}
}
def stringRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Option[String]] = { params =>
new RequestParam[Option[String]] {
def propagate: Boolean = willPropagate
def value: Option[String] = params.get(paramName).flatMap(_.headOption)
def toQueryParams: Seq[(String, String)] = value.map(paramName -> _).toSeq
}
}
In reality, the following would be a class constructor that takes this Map read from the query string as a parameter, but for simplicity's sake, I'm just defining a val:
val requestParams = Map("no_ads" -> Seq("true"), "edition" -> Seq("us"))
// In reality, there are many more possible parameters, but this is simplified
val options = ('adsDebug ->> booleanRequestParam("ads_debug", true)) ::
('hideAds ->> booleanRequestParam("no_ads", true)) ::
('edition ->> stringRequestParam("edition", false)) ::
HNil
object bind extends (RequestParamBuilder ~> RequestParam) {
override def apply[T](f: RequestParamBuilder[T]): RequestParam[T] = f(requestParams)
}
// Create queryable option values record by binding the request parameters
val boundOptions = options.map(bind)
This last statement does not work, and returns the error:
<console>:79: error: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[bind.type,shapeless.::[RequestParamBuilder[Boolean] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("adsDebug")],RequestParamBuilder[Boolean]],shapeless.::[RequestParamBuilder[Boolean] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("hideAds")],RequestParamBuilder[Boolean]],shapeless.::[RequestParamBuilder[Option[String]] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("edition")],RequestParamBuilder[Option[String]]],shapeless.HNil]]]]
val boundOptions = options.map(bind)
But assuming that worked, I would want to do the following:
object propagateFilter extends (RequestParam ~> Const[Boolean]) {
override def apply[T](r: RequestParam[T]): Boolean = r.propagate
}
object unbind extends (RequestParam ~> Const[Seq[(String, String)]]) {
override def apply[T](r: RequestParam[T]): Seq[(String, String)] = r.toQueryParams
}
// Reserialize a query string for options that should be propagated
val propagatedParams = boundOptions.values.filter(propagateFilter).map(unbind).toList
// (followed by conventional collections methods)
I don't know what I need to do to get that first .map call to work, and I suspect I'll be running into issues with the next two polymorphic functions.
Update: the FieldPoly helper actually doesn't do all that much work for you here, and you can accomplish the same thing without it (and without the Witness implicit):
import shapeless.labelled.{ FieldType, field }
object bind extends Poly1 {
implicit def rpb[T, K]: Case.Aux[
FieldType[K, RequestParamBuilder[T]],
FieldType[K, RequestParam[T]]
] = at[FieldType[K, RequestParamBuilder[T]]](b => field[K](b(requestParams)))
}
It's also worth noting that if you don't mind living dangerously, you can skip the return type (in both implementations):
object bind extends Poly1 {
implicit def rpb[T, K] = at[FieldType[K, RequestParamBuilder[T]]](b =>
field[K](b(requestParams))
)
}
But in general having an implicit method with an inferred return type is a bad idea.
As I mention in a comment above, Case isn't covariant, which means that your bind will only work if the elements of the HList are statically typed as RequestParamBuilder (in which case you don't have a record).
You could use .values to get the values out of the record, and you can then map over the result, but (as you note) this would mean you lose the keys. If you want to preserve the keys, you can use Shapeless's FieldPoly, which is designed to help out in this kind of situation:
import shapeless.labelled.FieldPoly
object bind extends FieldPoly {
implicit def rpb[T, K](implicit witness: Witness.Aux[K]): Case.Aux[
FieldType[K, RequestParamBuilder[T]],
FieldType[K, RequestParam[T]]
] = atField(witness)(_(requestParams))
}
Now options.map(bind) will work as expected.
I don't think there's a better way to write this at the moment, but I haven't been following the most recent Shapeless developments very closely. In any case this is reasonably clear, not too verbose, and it does what you want.
To answer the other question in your comment: this previous question is a starting point, but I'm not aware of a really good overview of the mechanics of the implementation of polymorphic function values in Shapeless. It's a good idea for a blog post.
I want to be able to have a map implementation of type [String,Any] in which i can store objects of different classes then i want to be able to do
import app.User;
....
usr = map.getAs("user",User)// with usr being an object of Class/Object User. If user is not of that type it throws an exception.
where user was previously stored in the map.
Is this possible?
I can even be a standalone method e.g
usr = getAs(map,"user",User)
Thanks
You can use ClassTag. I don't have a lot of experience with it, but something like that should work:
def getAs[T: ClassTag](m: Map[String, Any], key: String): T =
m(key).asInstanceOf[T]
Usage:
val map = Map[String, Any]("one" -> 1, "hello"-> "world")
getAs[String](map, "hello")
res1: String = world
I let you deal with the exception, you can simply wrap the asInstanceOf in a Try, or whichever strategy you prefer. I also recommend changing the return type to Option[T].
Thanks vptheron. That definitely works.
I took your advice and did it using Option[T] and used matching instead.
def getAsOption[T: ClassTag](m: Map[String, Any], key: String): Option[T] =
m.get(key) match {
case x : Option[T] => x
case _ => None
}
I wanted a mechanism to pass random stuff in the play framework and this looks like a good solution for me though the purists wont like it.
I think using TypeTag will do better job unless you will never cast the values into generic types.
import scala.reflect.runtime.universe._
trait Typed[A] {
val value: A
implicit val ttag: TypeTag[A]
}
def cast[A, B: TypeTag](v: Typed[A]): Option[B] = {
val tA = typeOf[A](v.ttag)
val tB = typeOf[B]
def vB = v.value.asInstanceOf[B]
if (tA <:< tB) Some(vB) else None
}
def getTyped[A, B, C: TypeTag](map: Map[A, Typed[B]], key: A): Option[C] = {
map get key flatMap cast[B, C]
}
I am trying to use Scala macros to create a case class map of single-parameter copy methods, with each method accepting a Play Json JsValue and a case class instance, and returning an updated copy of the instance. However, I am running into problems with the macro syntax for returning a function object.
Given a case class
case class Clazz(id: Int, str: String, strOpt: Option[String])
the intention is to create a map of the class's copy methods
implicit def jsonToInt(json: JsValue) = json.as[Int]
implicit def jsonToStr(json: JsValue) = json.as[String]
implicit def jsonToStrOpt(json: JsValue) = json.asOpt[String]
Map("id" -> (json: JsValue, clazz: Clazz) = clazz.copy(id = json),
"str" -> (json: JsValue, clazz: Clazz) = clazz.copy(str = json), ...)
I have found two related questions:
Using macros to create a case class field map: Scala Macros: Making a Map out of fields of a class in Scala
Accessing the case class copy method using a macro: Howto model named parameters in method invocations with Scala macros?
...but I am stuck at how I can create a function object so that I can return a Map[String, (JsValue, T) => T]
Edit: Thanks to Eugene Burmako's suggestion to use quasiquotes - this is where I'm currently at using Scala 2.11.0-M7, basing my code on Jonathan Chow's post (I switched from using (T, JsValue) => T to (T, String) => T to simplify my REPL imports)
Edit2: Now incorporating $tpe splicing
import scala.language.experimental.macros
implicit def strToInt(str: String) = str.toInt
def copyMapImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context):
c.Expr[Map[String, (T, String) => T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val methods = fields.map { field => {
val name = field.name
val decoded = name.decoded
q"{$decoded -> {(t: $tpe, str: String) => t.copy($name = str)}}"
}}
c.Expr[Map[Sring, (T, String) => T]] {
q"Map(..$methods)"
}
}
def copyMap[T]: Map[String, (T, String) => T] = macro copyMapImpl[T]
case class Clazz(i: Int, s: String)
copyMap[Clazz]
You got almost everything right in your code, except for the fact that you need to splice T into a quasiquote, i.e. to write $tpe instead of just T.
For that to look more natural, I usually explicitly declare type tag evidences in macros, e.g. def foo[T](c: Context)(implicit T: c.WeakTypeTag[T]) = .... After that I just write $T, and it looks almost fine :)
You might ask why quasiquotes can't just figure out that in the place where they're written T refers to the type parameter of a macro and then automatically splice it in. That would be very reasonable question, actually. In languages like Racket and Scheme, quasiquotes are smart enough to remember things about the lexical context they are written in, but in Scala this is a bit more difficult, because there are so many different scopes in the language. Yet, there's a plan to get there, and research in that direction in already underway: https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI.
I can't figure out how to deal with overriding "+" in an immutable map if the map can only store an invariant type for its values.
Something like:
class FixedMap(val impl : Map[String, Int])
extends immutable.Map[String, Int] with immutable.MapLike[String, Int, FixedMap] {
// This should return FixedMap if B1 is Int, and Map[String,B1]
// if B1 is a superclass of Int; but there's no way to do that.
// It is possible to return FixedMap here but then you have to
// throw at runtime if B1 is not Int
override def +[B1 >: Int](kv : (String, B1)) : Map[String, B1] = {
kv match {
case (k, v : Int) =>
new FixedMap(impl + Pair(k, v))
case _ =>
impl + kv
}
}
// ...
}
I'd like this to work like the methods that use CanBuildFrom and always keep the original type if possible. Is there a way? Or do Map subclasses always have to leave the value type as a type parameter?
Here's a complete compilable example:
import scala.collection.immutable
// pointless class that wraps another map and adds one method
class FixedMap(val impl : Map[String, Int])
extends immutable.Map[String, Int] with immutable.MapLike[String, Int, FixedMap] {
override val empty : FixedMap = FixedMap.empty
// This should return FixedMap if B1 is Int, and Map[String,B1]
// if B1 is a superclass of Int; but there's no way to do that.
// It is possible to return FixedMap here but then you have to
// throw at runtime if B1 is not Int
override def +[B1 >: Int](kv : (String, B1)) : Map[String, B1] = {
kv match {
case (k, v : Int) =>
new FixedMap(impl + Pair(k, v))
case _ =>
impl + kv
}
}
override def -(key : String) : FixedMap = {
new FixedMap(impl - key)
}
override def get(key : String) : Option[Int] = {
impl.get(key)
}
override def iterator : Iterator[(String, Int)] = {
impl.iterator
}
def somethingOnlyPossibleOnFixedMap() = {
println("FixedMap says hi")
}
}
object FixedMap {
val empty : FixedMap = new FixedMap(Map.empty)
}
object TestIt {
val empty = FixedMap.empty
empty.somethingOnlyPossibleOnFixedMap()
val one = empty + Pair("a", 1)
// Can't do the below because one is a Map[String,Int] not a FixedMap
// one.somethingOnlyPossibleOnFixedMap()
}
Here's what I'd try:
class FixedMap(val impl: immutable.Map[String, Int])
extends immutable.Map[String, Int] with immutable.MapLike[String, Int, FixedMap] {
override def +[B1 >: Int](kv: (String, B1)): immutable.Map[String, B1] = impl + kv
def +(kv: (String, Int))(implicit d: DummyImplicit): FixedMap = new FixedMap(impl + kv)
// ...
}
You're right: you cannot override the + that already exists, so you have to leave it there — otherwise your subclass wouldn't be able to do things that the superclasses can do, which violates the Liskov substitution principle. But you can add an additional method wich the exact arguments that you want (and you don't need a CanBuildFrom in this particular case).
The only problem is that the new method has the exact same type erasure as the one you were trying to override, but which has an incompatible signature. To solve this, you can add the DummyImplicit — defined in Predef as a class for which an implicit value is always available. Its main use is to work around type erasure in case of overloading like this.
Note that the static type of the map on which you want to call your overloaded method has to be FixedMap for this to work. If an object has a run-time type of FixedType but is statically typed to a regular Map[String, Int], the compiler won't call your new overloaded method.
It's possible to implement what you want using CanBuildFrom. The question is - do you really want/need to make it? The are several similar questions in SO, here is one of them (hope you will find answer there):
Extending Scala collections
Generally Scala gives use enough tools to avoid this (extending collections). And you really need a good reason to start with this.
It looks like it does work (best I can tell so far) if you add another + overload:
def +(kv : (String, Int))(implicit bf : CanBuildFrom[FixedMap, (String, Int), FixedMap]) : FixedMap = {
val b = bf(empty)
b ++= this
b += kv
b.result
}
The problem I was seeing doing this before was caused by returning FixedMap from the overridden +, thus preventing any upgrade to a generic map. I guess this allowed implicit conversion of the pair being +'d to work. But if you fix that overridden + method to return Map[String,B1] again, you can't use implicit conversion on the pair's value anymore. No way for the compiler to know whether to go to a superclass map i.e. Map[String,Any] or implicitly convert to Int in order to stick with FixedMap. Interesting that the return type of the method changes whether implicit conversions are used; I guess given a FixedMap return type the compiler can deduce that B1 is always just B (or something like that!).
Anyway this seems to be my bug; you just can't use implicit conversion on the pair._2 passed to + while being compatible with the Map interface, even conceptually. Now that I gave up on that, I think the overloaded + will work OK.