Implicits over type inference for object transformation - scala

Part of a current project involves converting from types coupled to a database and a generic type used when serializing the results out to clients via Json, the current implementation in Scala uses type inference to correctly perform the transformation, using Scala's TypeTag:
def Transform[A: TypeTag](objects:Seq[A]):Seq[Children] = typeOf[A] match {
case pc if pc =:= typeOf[ProductCategory] =>
TransformProductCategory(objects.asInstanceOf[Seq[ProductCategory]])
case pa if pa =:= typeOf[ProductArea] =>
TransformProductArea(objects.asInstanceOf[Seq[ProductArea]])
case pg if pg =:= typeOf[ProductGroup] =>
TransformProductGroup(objects.asInstanceOf[Seq[ProductGroup]])
case psg if psg =:= typeOf[ProductSubGroup] =>
TransformProductSubGroup(objects.asInstanceOf[Seq[ProductSubGroup]])
case _ =>
throw new IllegalArgumentException("Invalid transformation")
}
The types used as input are all case classes and are defined internally within the application, for example:
case class ProductCategory(id: Long, name: String,
thumbnail: Option[String],
image:Option[String],
sequence:Int)
This approach, although suitable at the moment, doesn't feel functional or scalable when potentially more DB types are added. I also feel using asInstanceOf should be redundant as the type has already been asserted. My limited knowledge of implicits suggests they could be used instead to perform the transformation, and remove the need for the above Transform[A: TypeTag](objects:Seq[A]):Seq[Children] method altogether. Or maybe there is a different approach I should have used instead?

You can define a trait like this:
trait Transformer[A] {
def transformImpl(x: Seq[A]): Seq[Children]
}
Then you can define some instances:
object Transformer {
implicit val forProduct = new Transformer[ProductCategory] {
def transformImpl(x: Seq[ProductCategory]) = ...
}
...
}
And then finally:
def transform[A: Transformer](objects:Seq[A]): Seq[Children] =
implicitly[Transformer[A]].transformImpl(objects)
Preferably, you should define your implicit instances either in Transformer object or in objects corresponding to your category classes.

I'm not sure how your program is supposed to work exactly, nor do I know if any of your Transforms are self-made types or something you pulled from a library, however I may have a solution anyway
Something match is really good with, is case classes
So rather than manually checking the type of the input data, you could maybe wrap them all in case classes (if you need to bring data with you) or case objects (if you don't)
That way you can do something like this:
// this code assumes ProductCategory, ProductArea, etc.
// all extends the trait ProductType
def Transform(pType: ProductType): Seq[Children] = pType match {
case ProductCategory(objects) => TransformProductCategory(objects)
case ProductArea(objects) => TransformProductArea(objects)
case ProductGroup(objects) => TransformProductGroup(objects)
case ProductSubGroup(objects) => TransformProductSubGroup(objects)
}
By wrapping everything in a case class, you can specify exactly which type of data you want to bring along, and so long as they (the case classes, not the data) all inherit from the same class/trait, you should be fine
And since there's only a little handful of classes that extend ProductType you don't need the default case, because there is no default case!
Another benefit of this, is that it's infinitely expandable; just add more cases and case classes!
Note that this solution requires you to refactor your code a LOT, so bare that in mind before you throw yourself into it.

Related

'with' keyword usage in scala with case classes

I though if something like this makes sense in scala:
object CaseClassUnion extends App {
case class HttpConfig(bindUrl: String, port: String)
case class DbConfig(url: String, usr: String, pass: String)
val combined: HttpConfig with DbConfig = ???
//HttpConfig("0.0.0.0", "21") ++ DbConfig("localhost", "root", "root")
//would be nice to have something like that
}
At least this compiles... Is there a way, probably with macros magic to achieve union of two classes given their instances?
In zio I believe there is something like in reverse:
val live: ZLayer[ProfileConfiguration with Logging, Nothing, ApplicationConfiguration] =
ZLayer.fromServices[ProfileConfigurationModule.Service, Logger[String], Service] { (profileConfig, logger) => ???
where we convert ProfileConfiguration with Logging to function of ProfileConfigurationModule.Service, Logger[String] => Service
Several things.
When you have several traits combined with with Scala does a trait linearization to combine them into one class with a linear hierarchy. But that's true for traits which doesn't have constructors!
case class (which is not a trait) cannot be extended with another case class (at all) because that would break contracts like:
case class A(a: Int)
case class B(a: Int, b: String) extends A(a)
A(1) == B(1, "") // because B is A and their respective fields match
B(1, "") != A(1) // because A is not B
B(1, "").hashCode != A(1).hashCode // A == B is true but hashCodes are different!
which means that you cannot even generate case class combination manually. You want to "combine" them, use some product: a tuple, another case class, etc.
If you are curious about ZIO it:
uses traits
uses them as some sort of type-level trick to represent an unordered set of dependencies, where type inference would calculate set sum when you combine operations and some clever trickery to remove traits from the list using .provide to remove dependency from the set
ZLayers are just making these shenanigans easier
so and if you even pass there some A with B you either combined it yourself by using cake pattern, or you passed dependencies one by one. ZIO developer might never be faced with the problem of needing some macro to combine several case classes (.provide(combineMagically(A, B, C, D, ...)) as they could pass implementations of each dependency one by one (.provide(A).provide(B)) and the code underneath would never need the combination of these types as one value - it's just a compile-time trick that might never translate to the requirement of an actual value of type A with B with C with D ....
TL;DR: You cannot generate a combination of 2 case classes; ZIO uses compound types as some sort of type-level set to trace dependencies and it doesn't actually require creating values of the compound types.

Shouldn’t `EitherT` be covariant? Especially in the Left?

I have the following code
def addKitten(kitten: Kitten): EitherT[Future, GenericError, UUID] = {
val futureOfEither = db.run { // returns a Future[Int] with the number of rows written
kittens += kitten
}.map {
case 1 => kitten.uuid.asRight
case _ => GenericError.SpecificError.asLeft
}
EitherT(futureOfEither)
}
Where SpecificError is a subclass of GenericError. For some reason it does not compile complaining that a SpecificError is not a GenericError. Is it right?
I mean, Either[A, B] should be immutable, so why not making it covariant? Am I missing something?
The same issue for XorT and OptionT was raised here. The reply was:
In Scala, variance has both positives and negatives (perhaps that's how they decided on variance notation! :P). These pros/cons have been discussed numerous times in various venues, so I won't go into them now, but in my opinion at the end of the day you kind of have to settle on "to each their own".
I think this "to each their own" perspective implies that you can't force variance on a type constructor. A concrete example is scalaz.Free in the 7.0 series. It forces the S type constructor to be covariant. At the time I often wanted to wrap a Coyoneda in Free. The most recent versions of Cats and Scalaz have the Coyoneda essentially built into the Free, so this particular use might not be as desired now, but the general principle applies. The problem is that Coyoneda is invariant, so you simply couldn't do this (without a mess of #uncheckedVariance)! By making type constructor parameters invariant, you may end up forcing people to be more explicit about types, but I think that it beats the alternative, where you can prevent them from being able to use your type at all.
Ok, I found a workaround.
I still have no idea why the EitherT is not covariant but you have to remember that the Either itself is covariant.
So the trick is to tell the compiler to use an upper bound for the EitherT creation:
EitherT[Future, GenericError, UUID](futureOfEither)
Having more than one error as left works too (because the compiler is forced to find the LUB) but GenericError has to extend Product and Serializable if it’s a trait and SpecificError is a case class (refer to this question as to why)
...
}.map {
case 1 => kitten.uuid.asRight
case 2 => GenericError.SpecificError2.asLeft
case _ => GenericError.SpecificError.asLeft
}
EitherT(futureOfEither)
As another quite neat workaround you could specify abstract types in flatMap[E, T] type parameters, for example:
// Given ADT
trait Err
case class E1() extends Err
case class E2() extends Err
// We could do (pseudo-code)
EitherT(E1()).flatMap[Err, Int] { x => 100 }
It is in case of FlatMap. For Map you could only transform value and type on the right side.

Why can't I apply pattern-matching against non-case classes?

Is there a better explanation than "this is how it works". I mean I tried this one:
class TestShortMatch[T <: AnyRef] {
def foo(t: T): Unit = {
val f = (_: Any) match {
case Val(t) => println(t)
case Sup(l) => println(l)
}
}
class Val(t: T)
class Sup(l: Number)
}
and compiler complaints:
Cannot resolve symbol 'Val'
Cannot resolve symbol 'Sup'
Of course if I add case before each of the classes it will work fine. But what is the reason? Does compiler make some optimization and generate a specific byte-code?
The reason is twofold. Pattern matching is just syntactic sugar for using extractors and case classes happen to give you a couple methods for free, one of which is an extractor method that corresponds to the main constructor.
If you want your example above to work, you need to define an unapply method inside objects Val and Sup. To do that you'd need extractor methods (which are only defined on val fields, so you'll have to make your fields vals):
class Val[T](val t: T)
class Sup(val l: Number)
object Val {
def unapply[T](v: Val[T]): Option[T] = Some(v.t)
}
object Sup {
def unapply(s: Sup): Option[Number] = Some(s.l)
}
And which point you can do something like val Val(v) = new Val("hi"). More often than not, though, it is better to make your class a case class. Then, the only times you should be defining extra extractors.
The usual example (to which I can't seem to find a reference) is coordinates:
case class Coordinate(x: Double, val: Double)
And then you can define a custom extractors like
object Polar {
def unapply(c: Coordinate): Option[(Double,Double)] = {...}
}
object Cartesian {
def unapply(c: Coordinate): Option[(Double,Double)] = Some((c.x,c.y))
}
to convert to the two different representations, all when you pattern match.
You can use pattern matching on arbitrary classes, but you need to implement an unapply method, used to "de-construct" the object.
With a case class, the unapply method is automatically generated by the compiler, so you don't need to implement it yourself.
When you write match exp { case Val(pattern) => ... case ... }, that is equivalent to something like this:
match Val.unapply(exp) {
case Some(pattern) =>
...
case _ =>
// code to match the other cases goes here
}
That is, it uses the result of the companion object's unapply method to see whether the match succeeded.
If you define a case class, it automatically defines a companion object with a suitable unapply method. For a normal class it doesn't. The motivation for that is the same as for the other things that gets automatically defined for case classes (like equals and hashCode for example): By declaring a class as a case class, you're making a statement about how you want the class to behave. Given that, there's a good chance that the auto generated will do what you want. For a general class, it's up to you to define these methods like you want them to behave.
Note that parameters for case classes are vals by default, which isn't true for normal classes. So your class class Val(t: T) doesn't even have any way to access t from the outside. So it isn't even possible to define an unapply method that gets at the value of t. That's another reason why you don't get an automatically generated unapply for normal classes: It isn't even possible to generate one unless all parameters are vals.

Using Scala's pattern matching to match parameterized types

object Helper {
case class IsMap(x: Map[String,Any])
case class IsT[T](t: T)
implicit class MapFunctions(val map: Map[String,Any]) {
def path[T](path : String*) : Option[T] = {
if(path.size == 1)
None
map.get(path.head).get match {
case IsMap(item) => item.path(path.tail:_*)
case IsT(item) => Option(item)
case _ => throw new Exception("Unexpected type encountered in path")
}
}
}
}
I'm still quite new to Scala. I want to to avoid Option(item.asInstanceOf[T]) in the IsT(item) portion of the pattern matching. I am using case classes to pattern match against parameterized types to stop the type erasure. What I want to do is be able to have IsT(item) match only if item is of type T. Running this code doesn't seem to match on it. What am I doing wrong?
Update: Up until tonight, I thought Scala had magically solved some of the nastier problems with type erasure that Java had. I knew there were still times when type erasure was present but I didn't realize it suffered pretty much the same. I played with the TypeTag a little bit. I was excited by the type interference in Scala and the ability to have it infer the type that I was trying to return but having val myType = path("blah", "blah").get screwed this up because the inferred type is Nothing which would cause a casting error. I ended up solving this the same way I had to solve it in java- have the path() method take Class[T] and use that as the parameterized return type. It's unfortunate... somehow I thought Scala was capable of so much more...
Anyways- my new method looks like this:
object Helper {
implicit class MapFunctions[K,V](val map: Map[K,V]) {
def path[T<:V](clazz: Class[T], pathParts: K*): Option[T] = {
if (pathParts.size == 0)
None
else {
map.get(pathParts.head).get match {
case im: Map[K, V] if pathParts.size > 1 => im.path(clazz, pathParts.tail: _*)
case i => Option(i.asInstanceOf[T])
}
}
}
}
}
Oh... and the purpose of this implicit? My team has spent the better part of a week trying to find a good json library in Scala. We've used json4s previously but aren't happy with the fact that it's container object stores a List of Tuple2 instead of a map and we're heaving on access so this isn't going to work for us. We've reverted to using Jackson and just having it deserialize the json to Map[String,Any]. I found myself doing things where I wanted to cascade down the map like a tree into nested objects and this "path" method just seemed reasonable to be able to do that.

Scala: Compare types

I want to define a function that could compare two class types. I have defined two different classes:
abstract class Task
case class DefinedTask(id: Long) extends Task
case class EmptyTask() extends Task
Then I have a function that returns an object of type Task, that could be either DefinedTask or EmptyTask.
Now, what I'd like to do is to have a function that identifies if this object is a DefinedTask or just an EmptyTask. The problem would be simple, I'd use pattern matching. But I want to generalize it because multiple classes go with this Defined/Empty pattern.
What I'd tried so far is:
def makeReturned[T: Manifest, U: Manifest](obj: T)(u: U)(f: T => Value) =
if(manifest[U] equals manifest[T]) f(obj) else
throw new IllegalArgumentException()
}
//call to it
makeReturned(task)(DefinedTask)(makeTask)
U is always DefinedTask, T could be either DefinedTask or EmptyTask, but it is returned as Task.
manifest[U].erasure.toString //"class DefinedTask$"
manifest[T].erasure.toString //"class Task"
Which is right from the compiler's stand point but it's not for me. So, my question is how could I compare them in a way that would give me what I want?
It looks like you want run-time, not compile-time checking. So I think you mean
def makeReturned[U <: Task, T <: Task](obj: T)(u: U)(f: T => Value) = {
if (obj.getClass.isInstance(u.getClass)) f(obj)
else throw new IllegalArgumentException
}
or something like that. Look at the methods on java.lang.Class and pick the one that does what you want.
There are some mistakes in your code:
You should make the abstract class Task sealed for exhaustive pattern matching
Empty case classes are deprecated. The compiler should have warned you about it. case object EmptyTask extends Task would be the correct alternative
Both case classes and case objects extend the trait Product. You can check the product on being empty with either of the following ways:
task.productArity == 0
task.productIterator.isEmpty
I think you'd be much better off reapproaching your problem. Why not just use the standard Option and have the instances of a simple case class Task(id: Int) wrapped in it? This could be the general approach for all the other similar entities of yours.