Type Erasure in Scala - scala

I'm rather confused about what's happening here:
import scala.collection.immutable._
object Main extends App {
sealed trait Node
sealed trait Group
case class Sheet(
val splat: String,
val charname: String,
val children: ListMap[String, Node],
val params0: ListMap[String, Param], //params0 to separate sheet-general parameters
val note: Option[Note]
) extends Node with Group
case class Attributes(val name: String) extends Node with Group
case class Param(val name: String, val value: String) extends Node
case class Note(val note: String) extends Node
I've got three versions of a replace function - the last one is the one I'm actually trying to write, the others are just debugging.
class SheetUpdater(s: Sheet) {
def replace1[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:Sheet) =>
case (_, _:Attributes) =>
}
}
}
This version throws no warnings, so apparently I have access to the type of s.children at runtime.
class SheetUpdater(s: Sheet) {
def replace2[T <: Group](g: T): Unit = {
g match {
case _:Sheet =>
case _:Attributes =>
}
}
}
Neither does this version, so apparently the details of g's type are also available at runtime...
class SheetUpdater(s: Sheet) {
def replace3[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
... but even so, this ends up throwing me the dreaded Abstract type pattern T is unchecked since it is eliminated by erasure warning. What's going on here?

In Scala, generics are erased at runtime, which means that the runtime type of List[Int] and List[Boolean] is actually the same. This is because the JVM as a whole erases generic types. All this is due because the JVM wanted to remain backwards compatible way back when generics were first introduced...
There is a way around this in Scala using a ClassTag, which is an implicit parameter that then can be threaded around with whatever generic you are using.
You can think of : ClassTag as passing the type of the generic as an argument. (It is syntactic sugar for passing an implicit parameter of type ClassTag[T].)
import scala.reflect.ClassTag
class SheetUpdater(s: Sheet) {
def replace3[T <: Group : ClassTag](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
Newer answers of this question have more details.

Related

How to chain generically chain functions returning Either with an operator like `andThen`?

Problem: Chaining multiple Either returning functions, the Left of which are all failures inheriting from a common sealed trait InternalError. However, the compiler complains that the chain is returning Either[_,Success] instead of Either[InternalError, Success].
Here's the code that does the chaining:
import scala.language.implicitConversions
object EitherExtension {
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
//get ability to chain/compose functions that return aligning Eithers
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
}
As was pointed out in the comments this discards the type of Left. If I change it the below it will not work since the final output can be of type Either[X|Y, C] which resolves to Either[_,C] and I'm back to square one.
implicit class AndThenEither[A,B,X](val e: (A) => Either[X, B]) {
def andThenE[C,Y](f:(B) => Either[Y, C]): (A) => Either[_, C] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
Here's the example showing the compositional failure of type alignment:
import EitherExtension._
object AndThenComposition {
//sample type definitions of failure responses
sealed trait InternalError
case class Failure1() extends InternalError
case class Failure2() extends InternalError
//sample type definitions
case class Id(id: Int)
case class Stuff()
//sample type definitions of successful responses
case class Output1()
case class Output2()
case class InputRequest()
val function1: (InputRequest) => Either[Failure1, Output1] = ???
val function2: (Output1) => Either[Failure2, Output2] = ???
def doSomething(s:Id, l:List[Stuff]): Either[InternalError, Output2] = {
val pipeline = function1 andThenE function2
pipeline(InputRequest()) //Why is this of type Either[_, Output2]
}
}
What am I missing? How can I get the return type to not be Either[Any, Output2] but rather the base/sealed trait? Is this possible to do generically?
You need to preserve the type of the left so we will modify the extension method to do that.
Note that, since both eithers can have different left types, what we will do is use a type bound to ask the compiler to infer the LUB between those types; thanks to Any this is always possibles (although not always helpful).
object EitherExtension {
implicit class AndThenEither[I, L1, R1](private val f: I => Either[L1, R1]) extends AnyVal {
def andThenE[L2 >: L1, R2](g: R1 => Either[L2, R2]): I => Either[L2, R2] =
i => f(i).flatMap(g)
}
}
Which can be used like this:
import EitherExtension._
object AndThenComposition {
sealed trait InternalError
final case object Failure1 extends InternalError
final case object Failure2 extends InternalError
val function1: Int => Either[Failure1.type, String] = ???
val function2: String => Either[Failure2.type, Boolean] = ???
def doSomething(input: Int): Either[InternalError, Boolean] = {
(function1 andThenE function2)(input)
}
}
See the code running here.
In case you're using this in production, and it's not just a learning thing, what you're looking for it's called Kleisli, and fortunately cats-core already implements it.
According to the cats-core docs:
Kleisli enables composition of functions that return a monadic value,
for instance an Option[Int] or a Either[String, List[Double]], without
having functions take an Option or Either as a parameter, which can be
strange and unwieldy.
Since Kleisli composes two functions with the signature A => F[B], you'd need only one abstraction to be able to use Kleisli, which is creating a new type for your operation:
type Operation[A] = Either[InternalFailure, A]
By doing this, you should be able to use Kleisli like this:
import cats.data.Kleisli
val first: Kleisli[Operation, InputRequest, Output1] = Kleisli { request: InputRequest =>
Left(Failure1())
}
val second: Kleisli[Operation, Output1, Output2] = Kleisli { output: Output1 =>
Right(Output2())
}
val composed = first.andThen(second)

Determining constructor parameter count and type via reflection in Scala

I have about a hundred small classes that inherit a trait. The classes are instantiated in a factory via reflection based on their names.
Class.forName(name).getConstructor().newInstance().asInstanceOf[Trait]
Now requirements have changed such that one and only one of the classes needs to take a parameter during construction. I am trying to change the factory to handle this new case. The REPL worksheet with my approach looks like this:
import java.lang.reflect.Constructor
trait T {
def p: Unit
}
class C1 extends T {
override def p = println("no ymd")
}
class C2(a: Array[String]) extends T {
override def p = println(s"year: ${a(0)}")
}
class C3(a: Array[Int]) extends T {
override def p = println(s"day: ${a(2)}")
}
val ymd = Array("2019","10","23")
val ymd_int = Array(2019,10,23)
def getT(c: Class[_]): T = {
c.getConstructors match {
case Array(c: Constructor[Array[String]]) => c.newInstance(ymd).asInstanceOf[T]
case Array(c: Constructor[Array[Int]]) => c.newInstance(ymd_int).asInstanceOf[T]
case Array(c: Constructor[_]) => c.newInstance().asInstanceOf[T]
case _ => throw new Exception("...")
}
}
getT(classOf[C1]).p
getT(classOf[C2]).p
getT(classOf[C3]).p
In the REPL, I'm using classOf instead of Class.forName because class names in the REPL are kind of wonky.
During compilation I'm getting the warnings:
Warning:(25, 23) non-variable type argument Array[String] in type
pattern java.lang.reflect.Constructor[Array[String]] is unchecked
since it is eliminated by erasure
case Array(c: Constructor[Array[String]]) =>
c.newInstance(ymd).asInstanceOf[T]
and
Warning:(26, 23) non-variable type argument Array[Int] in type
pattern java.lang.reflect.Constructor[Array[Int]] is unchecked
since it is eliminated by erasure
case Array(c: Constructor[Array[Int]]) =>
c.newInstance(ymd_int).asInstanceOf[T]
And then of course the calls to getT are failing because the three case statements look identical at run time and so all three calls are being handled by the first case.
Please Help.
Try
def getT(clazz: Class[_]): T = {
val constructor = clazz.getConstructors.head
(constructor.getParameterTypes.headOption.map(_.getSimpleName) match {
case None => constructor.newInstance()
case Some("String[]") => constructor.newInstance(ymd)
case Some("int[]") => constructor.newInstance(ymd_int)
case _ => throw new Exception("...")
}).asInstanceOf[T]
}

My function in Scala is returning the supertype and my type class can't handle it

I'm trying to go functional, but when working on real world problems I'm struggling, I need helps with a few basics. I like the idea of a type class and to add more implicit types in future.
trait Query {
def queryDetails: QueryDetails
}
case class LocalQueryType(queryDetails: QueryDetails) extends Query
case class JdbcQueryType(queryDetails: QueryDetails) extends Query
def queryTypeFactory(queryDetails: QueryDetails): Query = {
queryDetails.platform match {
case c if queryDetails.platform.contains("-file://") => LocalQueryType(queryDetails)
case _ => JdbcQueryType(queryDetails)
}
}
Then I have a type class that looks for the local or Jdbc types, but it doesn't work as it is receiving only Query type.
I've tried using generics like:
def queryTypeFactory[T<:Query](queryDetails: QueryDetails): T = {
queryDetails.platform match {
case c if queryDetails.platform.contains("-file://") => LocalQueryType(queryDetails)
case _ => JdbcQueryType(queryDetails)
}
}
Adding Type Class:
trait QueryTask[A] {
def runQuery(a: A): String
}
object QueryTask {
def apply[A](implicit sh: QueryTask[A]): QueryTask[A] = sh
object ops {
def runQuery[A: QueryTask](a: A) = QueryTask[A].runQuery(a)
implicit class ShowOps[A: QueryTask](a: A) {
def runQuery = QueryTask[A].runQuery(a)
}
}
implicit val localQuery: QueryTask[LocalQueryType] =
instance(localQueryType => s"running local: ${localQueryType.queryDetails.command} on platform: ${localQueryType.queryDetails.platform}")
implicit val jdbcQuery: QueryTask[JdbcQueryType] =
instance(jdbcQueryType => s"running jdbc: ${jdbcQueryType.queryDetails.command} on platform: ${jdbcQueryType.queryDetails.platform}")
def instance[A](func: A => String): QueryTask[A] =
new QueryTask[A] {
def runQuery(a: A): String = func(a)
}
The idea is to not use the usual OO factory or strategy pattern.
Type class approach seems not to work in your use case.
Implicits are resolved at compile time. So in order to decide which instance you need QueryTask[LocalQueryType] or QueryTask[JdbcQueryType] compiler has to know whether type A is LocalQueryType or JdbcQueryType at compile time.
But it seems you decide that depending on whether queryDetails.platform.contains("-file://") or not i.e. at runtime.
It seems you need usual pattern mathing. You should use type class pattern when it's necessary.

Implicit lookup for Typeclass of None is not compatible with Contravariant Typeclass of Option

I don't get the following code to compile and I am curious of what I did wrong.
I defined a Contravariant Jsonwriter Trait and a function accepting implicit writers:
trait JsonWriter[-A] {
def write(value: A): Json
}
object Json {
def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
writer.write(value)
}
Additionally I have defined some instances of these writers:
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
(value: String) => JsString(value)
implicit val doubleWriter: JsonWriter[Double] =
(value: Double) => JsNumber(value)
class OptionWriter[-T](writer: JsonWriter[T]) extends JsonWriter[Option[T]] {
def write(value: Option[T]): Json = {
value match {
case None => JsNull
case Some(x) => writer.write(x)
}
}
}
implicit def optionWriter[T](implicit writer: JsonWriter[T]):
JsonWriter[Option[T]] = new OptionWriter[T](writer)
}
Now I have written a test:
"write double Option" in {
Some(1.0).toJson should be(JsNumber(1.0))
None.toJson should be(JsNull)
}
The first test for Some(1.0) works fine
The second one for None throws:
Error:(40, 12) could not find implicit value for parameter writer: JsonWriter[None.type]
None.toJson should be(JsNull)
If you want to try the code my JsonType definitions for this example are:
sealed trait Json
final case class JsObject(get: Map[String, Json]) extends Json
final case class JsString(get: String) extends Json
final case class JsNumber(get: Double) extends Json
case object JsNull extends Json
None, if you dont say anything else, is a Option[Nothing], so OptionWriter[Nothing] needs a JsonWriter[Nothing]. If you try Json.toJson(Some(1)) is the same, there is no JsonWriter[Int].
On the other hand, Json.toJson(None:Option[String]) works, because OptionWriter[String] can get a JsonWriter[String].
I think it has to do with the fact that
case object None extends Option[Nothing] { ... }
if you do one of the following, it will work
toJson(Option.empty[Double])
toJson(None : Option[Double])
Note that the second one uses type ascription to put a face, so to speak, on the Nothing (which is a subtype of everything)

What is the idiomatic way in Scala to pattern match on a type hierarchy with TypeTag?

I have a type hierarchy and want to ask a lookup method for an implementation. I am having trouble doing this without resorting to an asInstanceOf call.
So giving a simple type hierarchy like so
trait Vehicle
trait Flying extends Vehicle
class Plane extends Flying
trait Driving extends Vehicle
class Car extends Driving
trait Swimming extends Vehicle
class Boat extends Swimming
my lookup method is like this
def findVehicle[V <: Vehicle](implicit tag: TypeTag[V]): Option[V] = {
val v = tag.tpe match {
case t if t =:= typeOf[Flying] => Some(new Plane)
case t if t =:= typeOf[Driving] => Some(new Car)
case t if t =:= typeOf[Swimming] => Some(new Boat)
case _ => None
}
v.map(_.asInstanceOf[V])
}
with a lookup like so
println(findVehicle[Flying]) // Some(Plane#5b7fd935)
Is it possible to achieve a lookup like this without the asInstanceOf at the end?
You can avoid using TypeTag and use a type class instead.
trait Builder[+T] {
def build: Option[T]
}
implicit val flyingBuilder = new Builder[Flying] {
def build = Some(new Plane)
}
implicit val drivingBuilder = new Builder[Driving] {
def build = Some(new Car)
}
implicit val swimmingBuilder = new Builder[Swimming] {
def build = Some(new Boat)
}
implicit val anyBuilder = new Builder[Nothing] {
def build = None
}
def findVehicle[V <: Vehicle](implicit b: Builder[V]): Option[V] = b.build
No reflection involved and it's definitely more idiomatic.
Note how defining a Builder[Nothing] reproduces the same behavior you achieved by returning a None. This is not necessarily a good idea, as you're now forced to check whether the method was able to produce a value or not.
I would rather prefer a compile-time error if it's impossible to build the instance of the desired type, and you achieve it by directly returning T as opposed to Option[T] from build (and of course getting rid of the Nothing case).