I'm using Scala Play to process some JSON. I tried this:
val pair = ("foo", "bar")
val json = Json.obj(pair)
This gives me
type mismatch; found : (String, String) required: (String, play.api.libs.json.Json.JsValueWrapper)
Okay, fair enough. But if I do this instead:
val json = Json.obj(pair._1 -> pair._2)
Everything is fine. But I thought x -> y was just syntactic sugar for (x, y). It looks as if the latter expression is just constructing a new pair that's identical to the original and passing it to Json.obj, but something is clearly different between the two cases. What's going on?
The method obj takes tuples of type (String, JsValue)*. When you passed a tuple of (String, String), the compiler complained of type mismatch. (String, String) is not same as (String, JsValue)
Now, from docs
Most values don’t need to be explicitly wrapped by JsValue classes,
the factory methods use implicit conversion
The Play JSON API provides implicit Writes for most basic types,
such as Int, Double, String, and Boolean
When you passed String -> String, compiler has two options to implicitly convert String -> String to a tuple (String, String) or from String -> String to String -> JsValue to a tuple (String, JsValue). The second option matches the type required for the obj method and compiler follows that way to get a error free code.
Related
I'm trying to write a method that will allow Jackson ObjectMapper readValue on a json string to a parameterized object type. Something like this
case class MyObj(field1: String, field2: String)
val objectMapper: ObjectMapper = new ObjectMapper().registerModule(new DefaultScalaModule)
def fromJson[T](jsonString: String, objTyp: T): T = {
objectMapper.readValue(jsonString, classOf[T])
}
val x = fromJson("""{"field1": "something", "field2": "something"}""", MyObj)
This of course returns an error of
class type required but T found
i've looked at this issue Scala classOf for type parameter
but it doesn't seem to help. It seems like this is possible to do somehow. Looking for any help
You have to give it the actual runtime class to parse into, not just a type parameter.
One way to do it is passing the class directly:
def fromJson[T](json: String, clazz: Class[T]) = objectMapper.readValue[T](json, clazz)
val x = fromJson("""...""", classOf[MyObj])
Alternatively, you can use ClassTag, which looks a bit messier in implementation, but kinda prettier at call site:
def fromJson[T : ClassTag](json: String): T = objectMapper.readValue[T](
json,
implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]
)
val x = fromJson[MyObj]("""{"field1": "something", "field2": "something"}""")
i've looked at this issue Scala classOf for type parameter but it doesn't seem to help.
In the very first answer there it's written classTag[T].runtimeClass as a replacement of classOf[T]. This should help.
Regarding the signature
def fromJson[T](jsonString: String, objTyp: T): T
You should notice that MyObj has type MyObj.type (companion-object type), not MyObj (case-class type).
Class companion object vs. case class itself
So if you call fromJson("""...""", MyObj) then the types in these two places
def fromJson[...](jsonString: String, objTyp: ???): ???
^^^ ^^^ <--- HERE
can't be the same.
If it's enough for you to call
fromJson("""...""", classOf[MyObj])
or
fromJson[MyObj]("""...""")
(normally it should be enough) then please see #Dima's answer, you should prefer those options, they're easier.
Just in case, if you really want to call like fromJson("""...""", MyObj) then for example you can use the type class ToCompanion (this is more complicated) from
Invoke construcotr based on passed parameter
Get companion object of class by given generic type Scala (answer)
// ToCompanion should be defined in a different subproject
def fromJson[C, T](jsonString: String, objTyp: C)(implicit
toCompanion: ToCompanion.Aux[C, T],
classTag: ClassTag[T]
): T =
objectMapper.readValue(jsonString, classTag.runtimeClass.asInstanceOf[Class[T]])
val x = fromJson("""{"field1": "something", "field2": "something"}""", MyObj)
// MyObj(something,something)
I have two different maps that are created in my project. I have used type alias for both.
type alias1 = Map[String, ScalaObject]
type alias2 = Map[String, String]
I have a match-case situation where I want to differentiate between the two because different operations need to happen on both.
val obj: T = fromJson[T](jacksonMapper, json)
obj match {
case _: alias1 => operation1()
case _: alias2 => operation2()
case _ => obj
}
Any idea how to differentiate the two?
ADT would be to create a sealed trait with some case classes for each case; so one for a Map[String, String] and other for Map[String, ScalaObject] and you can pattern match on the case classes.
So like this:
sealed trait MyType extends Product with Serializable
final case class ObjMap(data: Map[String, ScalaObject]) extends MyType
final case class StrMap(data: Map[String, String]) extends MyType
val obj: MyType = ???
obj match {
case ObjMap(_) => operation1()
case StrMap(_) => operation2()
}
The Typeclass approach may be too complex for this.
The two key concepts to understand why the two maps cannot be differentiated at runtime are
type erasure where compile-time parameteriseed type Map[String, String] becomes runtime class Map
pattern matching translates to runtime isInstanceOf and asInstanceOf calls
Consider the following simplified example
case class Foo[T](i: T)
val fooInt = Foo[Int](42)
val fooStr = Foo[String]("")
fooInt.isInstanceOf[Foo[String]]
// val res0: Boolean = true
Note how isInstanceOf could not check at runtime what was the compile-time type parameter T and so it cannot differentiate between fooInt and fooStr. Effectively the best we can do is something like
fooInt.isInstanceOf[Foo[_]]
where the underscore _ serves to communicate the fact of type erasure.
Next consider how the following pattern match
(fooInt: Any) match { case str: Foo[String] => "oops :(" }
// val res1: String = oops :(
effectively becomes something like
if (fooInt.isInstanceOf[Foo[String]]) "oops :("
which again due to type erasure incorrectly evaluates to "oops :(".
An alternative approach is to try to do as much as possible at compile-time before type arguments are discarded. Typeclasses can be thought of as kind of compile-time pattern matching that happens before type erasure.
I am reading example about monad transformers in scalaz. Here is a piece of code:
scala> def myName(step: String): Reader[String, String] = Reader {step + ", I am " + _}
myName: (step: String)scalaz.Reader[String,String]
scala> def localExample: Reader[String, (String, String, String)] = for {
a <- myName("First")
b <- myName("Second") >=> Reader { _ + "dy"}
c <- myName("Third")
} yield (a, b, c)
localExample: scalaz.Reader[String,(String, String, String)]
scala> localExample("Fred")
res0: (String, String, String) = (First, I am Fred,Second, I am Freddy,Third, I am Fred)
How does it happen, that localExample can take a parameter? Oo There is not any parameters list in definition
localExample: Reader[String, (String, String, String)] just return type is specified. Also the same question about
myName function, it also has more function parameters then mentioned in its definition.
The key is in the return type that is specified, which is an instance of the Reader monad. Roughly, the reader monad is like a wrapper around a function from - in the case of myName - String to String, so it is a bit like a function defined as:
def myName(step: String): String => String
For localExample, the return type is now a "wrapper around a function from String to Triple of String" (quotes used because this is something of a simplified sense of what the Reader monad is).
In both cases, the return type is - sort of - a function that can take a (String) parameter with which to generate a final result, and it is this sort-of-a function that is taking the parameter passed to localExample in the call localExample("Fred"). Essentially like calling localExample().apply("Fred").
There is more to monads like the Reader than just the above (which I'm not sure I feel expert enough to explain properly right now), but hopefully that's enough to answer your question.
I've just started Scala and am tinkering in worksheets. For example:
def merp(str: String) : String = s"Merrrrrrrp $str"
val merp2 = (str: String) => s"Merrrrrrrp $str"
val merp3 = (str: String) => merp(str)
val merp4 = merp _
merp("rjkghleghe")
merp4("rjkghleghe")
And the corresponding worksheet results:
merp: merp[](val str: String) => String
merp2: String => String = <function1>
merp3: String => String = <function1>
merp4: String => String = <function1>
res0: String = Merrrrrrrp rjkghleghe
res1: String = Merrrrrrrp rjkghleghe
Saying, for example, val merp5 = merp produces an error, because apparently methods cannot be values the way functions can. But I can still pass methods as arguments. I demonstrate this in the following code snippet, adapted from a similar SO question:
def intCombiner(a: Int, b: Int) : String = s"herrrrrrp $a derrrrrrp $b"
def etaAbstractor[A, B](combineFoo: (A, B) ⇒ String, a: A, b: B) = combineFoo(a, b)
etaAbstractor(intCombiner, 15, 16)
worksheet result:
intCombiner: intCombiner[](val a: Int,val b: Int) => String
etaAbstractor: etaAbstractor[A,B](val combineFoo: (A, B) => String,val a: A,val b: B) => String
res10: String = herrrrrrp 15 derrrrrrp 16
Is methods-not-being-first-class a limitation, perhaps imposed by Scala's JVM interaction, or is it a decision in the language's design?
Why do I need to roll my own eta abstractions, as in merp3?
Is merp4 also an eta abstraction, or is it something sneakily similar?
Why does my etaAbstractor work? Is Scala quietly replacing intCombiner with intCombiner _?
Theoretical, computer sciencey answers are welcome, as are pointers to any relevant points in the language specification. Thanks!
Disclaimer: I'm not a computer scientist, but I will try to guess:
Method is a part of an object and doesn't exist outside of it. You can't pass method alone. Closure is another (equivalent?) way of encapsulating state, by converting an object method to a standalone function (which is by the way just another object with apply() method in Scala) you are creating a closure. This process is known as eta-expansion. §3.3.1, §6.26.5
You don't have to. You can also write val merp3 : (String => String) = merp. §6.26.5
Yes, merp4 is eta-expansion too. §6.7
§6.26.2
The reason it works with etaAbstractor is that the compiler can infer that a function (not a function invocation) is required.
If I had to guess why the underscore is required where a function type cannot be inferred, I'd think that it's to improve error reporting of a common class of errors (getting functions where invocations are intended). But again, that's just a guess.
In the JVM, a method is not an object, whereas a first-class function must be one. So the method must be boxed into an object to convert it to a function.
I have a function called or for example, which is defined as;
or(filters: FilterDefinition*)
And then I have a list:
List(X, Y, Z)
What I now need to do is call or like
or(func(X), func(Y), func(Z))
And as expected the length of the list may change.
What's the best way to do this in Scala?
Take a look at this example, I will define a function printme that takes vargs of type String
def printme(s: String*) = s.foreach(println)
scala> printme(List("a","b","c"))
<console>:9: error: type mismatch;
found : List[String]
required: String
printme(List(a,b,c))
What you really need to un-pack the list into arguments with the :_* operator
scala> val mylist = List("1","2","3")
scala> printme(mylist:_*)
1
2
3