how do I extract the right type from my pseduo-union type - scala.js

I have a psuedo union type returned from a js.
Here's my scalajs:
#js.native
trait ErrorResponse extends js.Object {
val error: String = js.native
val errorDescription: String = js.native
}
#js.native
trait TokenResponse extends js.Object{
val accessToken: String = js.native
val expriseOn: js.Date = js.native
val tokenType: String = js.native
val userId: String = js.native
val identityProvider: String = js.native
}
and my scalajs
#js.native
#JSImport("mymodule", "JSClassInModule")
class JSClassInModule extends js.Object {
// ...
def scalajsfunc(): ErrorResponse | TokenResponse = js.native
// ..
}
If I call scalajsfunc, how do I idiomatically boil down the returned value to one of the two classes specified in my pseudo-union class?

Use pattern matching:
val jsClassInModule: JSClassInModule = ???
(jsClassInModule.scalajsfunc(): Any) match {
case response: ErrorResponse => ...
case response: TokenResponse => ...
}
The ascription : Any is necessary to work around the fact that the Scala type checker does not know about the specific semantics of |, and gives spurious error messages if you try to directly match on a |.
Edit: If the individual types are JS traits, i.e., traits that extend js.Any, the above will not work, as in general it is not possible to perform isInstanceOf tests with JS traits (which is what pattern matching does, and is mostly unrelated to |). Instead, you need to code up whatever instance test makes sense, then cast. For example, assuming we can tell an ErrorResponse from a TokenResponse by testing for the existence of an errorCode property, we could do:
val jsClassInModule: JSClassInModule = ???
(jsClassInModule.scalajsfunc(): Any) match {
case response if js.Object.hasOwnProperty(response, "errorCode") =>
val errorResponse = response.asInstanceOf[ErrorResponse]
...
case response =>
val tokenResponse = response.asInstanceOf[TokenResponse]
...
}
Such is the ugly nature of JavaScript.

Related

Is there some way in scala that I can return a type?

I have a lot of classes such as DataFrameFlow, TextFlow, RDDFlow. They all derive from base class Flow.
Now I want to write a function judgeFlow which can read from a path: String and return something representing exact Flow type from which I can create corresponding instance. The whole code seems like the following
def judgeFlow(path:String) = /*1*/ {
Flow.getStoreType(path) match {
case StoreType.tdw =>
DataFrameFlow
case StoreType.hdfs =>
TextFlow
}
}
def createFlow(typeInfo:/*2*/) = /*3*/{
new typeInfo()
}
However, I don't know how to write in place 1, 2 and 3.
EDIT
Knowing how to construct them is not enough here, because I also want the following:
pattern matching through typeInfo
some ways to do asInstanceOf
EDIT 2
Definition of Flow
abstract class Flow(var outputName: String) extends Serializable{
def this() = this("")
...
}
Definition of DataFrameFlow
class DataFrameFlow(d: DataFrame, path: String) extends Flow {
var data: DataFrame = d
def this(data: DataFrame) = this(data, "")
def this(path: String) = this(null, path)
def this() = this(null, "")
...
}
Pattern matching can't return different types from different cases. The type returned by pattern matching is the least upper bound of types returned in cases.
When someone wants to return different types, most probably he/she wants a type class.
sealed abstract class Flow
class DataFrameFlow extends Flow
class TextFlow extends Flow
class RDDFlow extends Flow
trait JudgeFlow[In] {
type Out <: Flow
def judgeFlow(in: In): Out
}
object JudgeFlow {
implicit val `case1`: JudgeFlow[???] { type Out = DataFrameFlow } = ???
implicit val `case2`: JudgeFlow[???] { type Out = TextFlow } = ???
implicit val `case3`: JudgeFlow[???] { type Out = RDDFlow } = ???
}
def judgeFlow[In](in: In)(implicit jf: JudgeFlow[In]): jf.Out = jf.judgeFlow(in)
But the trouble is that types are resolved at compile time. You seem to want to choose a case based on a value of string i.e. at runtime. So you can't return more specific types than just Flow at compile time.
flatMap with Shapeless yield FlatMapper not found
It's hard to guess your use case completely.
But using Scala reflection you can try
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
def judgeFlow(path:String): Type = {
Flow.getStoreType(path) match {
case StoreType.tdw =>
typeOf[DataFrameFlow]
case StoreType.hdfs =>
typeOf[TextFlow]
}
}
def createFlow(typeInfo: Type): Flow = {
val constructorSymbol = typeInfo.decl(termNames.CONSTRUCTOR).asMethod
val classSymbol = typeInfo.typeSymbol.asClass
val classMirror = currentMirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror().asInstanceOf[Flow]
}

Select type class dynamically in Scala

I want to serialize and deserialize case classes to JSON, and after deserializing them, based on their subtype, I'd like to handle them differently. Something like this:
sealed trait Salutation
case class Hello(name: String)
case class Greetings(name: String)
case class HeyThere(name: String)
trait Handler[A] {
def greet(salutation: A)
}
implicit val helloSalutation: Handler[Hello] =
new Handler[Hello] {
def greet(salutation: Hello) = println(s"Hello, ${salutation.name}")
}
implicit val greetingsSalutation: Handler[Greetings] =
new Handler[Greetings] {
def greet(salutation: Greetings) = println("Greetings.")
}
implicit val heyThereSalutation: Handler[HeyThere] =
new Handler[HeyThere] {
def greet(salutation: HeyThere) = println("Hey there!")
}
val action = deserializeSalutationFromJson[Salutation](jsonStringFromSomewhere)
// NOTE: _action_ will be of the correct subtype, e.g. Greeting
/* TODO val handler = select which handler to use based the type of action */
handler.greet(action)
What is a good pattern in Scala for doing this of selection based on a dynamic type from JSON?

Scala: determine method result type for use in generics

In a third-party library there is a series of request classes, all of which derive from some common base class, which is generic and takes response class as a parameter:
abstract class AbstractRequest[ResponseType] {
…
def execute(): ResponseType
}
class UserList {…}
class UserListRequest extends AbstractRequest[UserList] {…}
class Avatar {…}
class AvatarRequest extends AbstractRequest[Avatar] {…}
…
I want to write some generic method that takes a request instance, executes it several times in some special ways and delegates processing of the responses to a function supplied in arguments:
def specialMultiExecute(request: Req)(responseHandler: Resp => Unit): Unit = …
— to be called like:
val myRequest: UserListRequest = …
specialMultiExecute(myRequest){ userList => … }
The problem is that I need to somehow specify Req and Resp types in the specialMultiExecute declaration. I tried the obvious approach:
def specialMultiExecute[Req <: AbstractRequest[Resp], Resp](request: Req)(responseHandler: Resp => Unit): Unit = …
— but Scala compiler fails to deduct generic argument types (an explicit specification like specialMultiExecute[UserListRequest, UserList](myRequest){ userList => … } is required).
In C++ in such case I could write a template function with a single template parameter Req, while making Resp to be determined as result type of the method Req::execute:
template<typename Req>
void specialMultiExecute(
Req request,
std::function<void (decltype(std::declval<Req>().execute()))> responseHandler
) {…}
//i.e. we use `decltype(std::declval<Req>().execute())` instead of Resp
Is there way to write something similar is Scala?
I mean something like (in Scala-like pseudocode):
def specialMultiExecute[Req <: AbstractRequest](request: Req)(responseHandler: ResultTypeOf(Req#execute) => Unit): Unit = …
It is a limitation of the type inference mechanism.
The simplest way to solve it is to use an implicit evidence that Req is a subtype of AbstractRequest[ResponseType].
Here is an example.
import scala.language.implicitConversions
import scala.reflect.runtime.universe.TypeTag
abstract class AbstractRequest[ResponseType] {
def execute(): ResponseType
}
final case class User(id: Int, name: String)
final case class House(id: Int, price: Int)
class UserListRequest extends AbstractRequest[List[User]] {
override def execute(): List[User] = List(User(id = 3, name = "Sasha"))
override def toString: String = "UserListRequest"
}
final class RequestWrapper[Req, Resp](val request: Req) extends AnyVal {
type ResponseType = Resp
}
implicit def request2wrapper[Req, Resp](request: Req)(implicit ev: Req <:< AbstractRequest[Resp]): RequestWrapper[Req, Resp] =
new RequestWrapper(request)
def specialMultiExecute[Req, Resp](wrapper: RequestWrapper[Req, Resp])
(responseHandler: wrapper.ResponseType => Unit)
(implicit ev: Req <:< AbstractRequest[Resp], TTReq: TypeTag[Req], TTResp: TypeTag[Resp]): Unit = {
val request: Req = wrapper.request
val executionResult: Resp = request.execute()
responseHandler(executionResult)
println(TTReq)
println(TTResp)
println(request)
}
specialMultiExecute(new UserListRequest())(println)
// List(User(3,Sasha))
// TypeTag[UserListRequest]
// TypeTag[List[User]]
// UserListRequest
Reference for <:<.
Reference for "Dependent types".
Edit
Te above code example was modified to allow identification of the concrete Request and Response types being used.

how to pattern match event.data in scalajs

I would like to match different events on my window#postMessage handler but I can not match native traits. So how could I solve this:
#js.native
trait XY extends js.Object {
val x: AnyVal = js.native
val y: AnyVal = js.native
}
#js.native
trait DataEvent extends js.Object {
val c: String = js.native // chart
val s: String = js.native // dataSet
val d: XY = js.native // xy data
val x: Seq[XY] = js.native // eXtra data
}
object ChartApi extends JSApp {
def receiveMessage(event: Any): Unit = {
event match {
case DataEvent => ???
case _ => println("else")
}
println(s"lala $event")
}
def main(): Unit = {
// add window event handler
dom.window.addEventListener("message", (event: MessageEvent) => receiveMessage(event.data), false)
println("Hello world!")
}
}
I have different charts in different iframes and I need to send events like "add this data" or "clear data" via postMessage.
JS traits do not have an identity at run-time, which is why you cannot directly test whether an arbitrary object is an instance of a JS trait.
Instead, you need to go the JS way, and test for the presence of a field that uniquely identifies the interface represented by the trait, among other possible choices. For example:
if (event.hasOwnProperty("d")) {
// it must be a DataEvent, because the alternatives do not have a 'd' field
val e = event.asInstanceOf[DataEvent]
...
} else {
...
}
Usually, we don't like to do that, though. So in a messaging protocol with several kinds of messages like this, we typically have an explicit messageType field or something like that. If you make sure that all DataEvent messages a messageType = "data" field, you can do:
if (event.messageType == "data") {
val e = event.asInstanceOf[DataEvent]
...
} else {
...
}
You can encode this "type test" in an extractor:
object DataEvent {
def unapply(event: Any): Option[DataEvent] =
if (event.messageType == "data") Some(event.asInstanceOf[DataEvent])
else None
}
which you can then use in a pattern match like this:
event match {
case DataEvent(event) => ???
case _ => println("else")
}
Finally, you may want to consider a serialization/pickling library to take care of all your messages as case classes instead. See https://www.scala-js.org/libraries/libs.html for a list of such libraries.

Optional conversion of basic types in scala

I am trying to support an abstraction of an ID type for a framework. Example here:
object AmINecessary {
case class StringConverter[T](op: String => T)
implicit val toInt = new StringConverter[Int](_.toInt)
implicit val toLong = new StringConverter[Long](_.toLong)
}
class Foo[ID] {
// ID can be String, Long, or Int
import AmINecessary._
// If ID is string, return string, otherwise convert to ID
def getID(id: String)(implicit c: StringConverter[ID] = null): ID = if (c == null) id.asInstanceOf[ID] else c.op(id)
}
This is then used as:
val fooString = new Foo[String]
val fooLong = new Foo[Long]
val fooInt = new Foo[Int]
fooString.getID("asdf") // "asdf":String
fooLong.getID("1234") // 1234:Long
fooInt.getID("1234") // 1234:Int
fooInt.getID("asdf") // java.lang.NumberFormatException
This works as expected. My questions are:
using an optional implicit by defaulting it to null then branching on it feels bad. What is the scala way to accomplish that?
Is it really necessary to write implicit conversions for a string to long or int?
I think the best option would be to simply add an implicit StringConverter[String] and remove the default null value.
That way your fooString works without risking a ClassCastException for every other type.
object AmINecessary {
case class StringConverter[T](op: String => T)
implicit val toInt = new StringConverter[Int](_.toInt)
implicit val toLong = new StringConverter[Long](_.toLong)
implicit val idConverter = new StringConverter[String](identity)
}
class Foo[ID] {
import AmINecessary.StringConverter
def getID(id: String)(implicit c: StringConverter[ID]): ID = c.op(id)
}
Regarding your question 2, the type class approach is not really necessary (but note that there are no implicit conversions here). You can also do it like this:
abstract class Foo[ID] {
def getID(id: String): ID
}
class FooInt extends Foo[Int] {
def getID(id: String) = id.toInt
}
class FooLong extends Foo[Long] {
def getID(id: String) = id.toLong
}
class FooString extends Foo[String] {
def getID(id: String) = id
}
1) About implicit defaulting to null, you could just:
object Unsafe {
implicit val toT[T] = new StringConverter[T](_.asInstanceOf[T])
}
2) It doesn't seem as a good idea. First, because you're hiding asInstanceOf, which is unsafe operation (potential runtime exception). Second, the more explicit conversion is - the better.
If you expect some complex transformations - it's better to return option from your getID method:
def getId[T](id: String)(converter: Option[StringConverter] = None) = converter.map(_.op(id))
However, default parameters aren't the best approach either. I'd stick with compile-time error requiring user to either write own converter or explicitly do an asInstanceOf, in general.
In your concrete case asInstanceOf doesn't make much sense as the only type it would work for is String, like getId[String], so what's the point of calling getId then?