Scala error when providing subclass instance in place of superclass? - scala

I am just trying things out in scala and I wrote this code
object Main:
def main(args: Array[String]): Unit =
val dInt: Data[Int] = IntData(1)
val dString: Data[String] = StringData("hello")
val Data(deconstructedInt) = dInt // unapply
val Data(deconstructedString) = dString // unapply
println(deconstructedInt)
println(deconstructedString)
sealed trait Data[+T]:
def get: T
case class IntData(get: Int) extends Data[Int]
case class StringData(get: String) extends Data[String]
object Data:
def apply[T](input: T): Data[T] = input match {
case i: Int => IntData(i) //compile error here
case s: String => StringData(s) //compile error here
}
def unapply[T](d: Data[T]): Option[String] = d match {
case IntData(get) => Some(s"int data => get = $get")
case StringData(get) => Some(s"string data => get = $get")
}
I get at the location commented in the code this error
Found: IntData
Required: Data[T]
case i: Int => IntData(i)
why I am getting this error, isn't IntData (or StringData) a subclass of Data ?

IntData is a subtype of Data[Int]. So if T is not Int, IntData is not a subtype of Data[T]. Now, you might say, if input matches the first case, then clearly T is Int. But the compiler is not smart to realise this!
You could try using Scala 3's new match types:
type DataOf[T] = T match {
case Int => IntData
case String => StringData
}
def apply[T](input: T): DataOf[T] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
Another alternative is union types:
def apply(input: Int | String): Data[Int | String] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
However, you will lose type information in doing this. Using the match types solution, apply(42) is inferred to have type IntData. Using the union types solution, it is inferred to have type Data[Int | String].

This way compiler connects the dots between Ts and it works:
object Main:
def main(args: Array[String]): Unit =
val dInt: Data[Int] = IntData(1)
val dString: Data[String] = StringData("hello")
val Data(deconstructedInt) = dInt // unapply
val Data(deconstructedString) = dString // unapply
println(deconstructedInt)
println(deconstructedString)
sealed trait Data[+T]:
def get: T
case class IntData[T <: Int](get: T) extends Data[T]
case class StringData[T <: String](get: T) extends Data[T]
object Data:
def apply[T](input: T): Data[T] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
def unapply[T](d: Data[T]): Option[String] = d match {
case IntData(get) => Some(s"int data => get = $get")
case StringData(get) => Some(s"string data => get = $get")
}

Related

How to find class parameter datatype at runtime in scala

import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
def getType[T: TypeTag](obj: T) = typeOf[T]
case class Thing(
val id: Int,
var name: String
)
val thing = Thing(1, "Apple")
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
}
Not able to digest why result is Absurd instead of I am int?
My aim is to know data-type of class parameter at runtime and match it to predefined types.
Both current dataType and typeOf[Int] are printed as Int but if you do showRaw you'll see why they don't match
showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())
The thing is that just the type Int and the type of nullary method returning Int are different types.
Try to add .resultType
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
} // I am Int
It's also worth mentioning that .decl(TermName("id")) returns getter symbol, it's .decl(TermName("id ")) (with a blank space) that returns field symbol. So alternatively you can do with a blank space in the symbol name and without .resultType
val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature
I'll add to #TomerShetah's answer that if the goal is "pattern matching" all fields of a case class then this can be done also at compile time (mostly) with Shapeless:
import shapeless.Poly1
import shapeless.syntax.std.product._
object printTypes extends Poly1 {
implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t"))
implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple
https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ
#Dmytrio answer is a great explanation why the reflection didn't work as you expected.
I can understand from your question, that what you are trying to do, is actually pattern match all variables you have in a case class. Please consider doing it in the following way:
case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")
thing.productIterator.foreach {
case t: Int => println(s"I am Int: $t")
case t: String => println(s"String, Do some stuff: $t")
case t => println(s"Absurd: $t")
}
Code run at Scastie.

How to apply a function on each field of a case class

Let's consider a classification problem :
object Classify extends App {
type Tag = String
type Classifier[A] = A => Set[Tag]
case class Model(a: Int, b: String, c: String, d: String)
def aClassifier : Classifier[Int] = _ => Set("A", "a")
def bClassifier : Classifier[String] = _ => Set("B")
def cClassifier : Classifier[String] = _ => Set("C")
def modelClassifier : Classifier[Model] = {
m => aClassifier(m.a) ++ bClassifier(m.b) ++ cClassifier(m.c)
}
println(modelClassifier(Model(1,"b", "c", "d")))
}
Is there a smarter way to implement modelClassifier using scalaz ?
As an idea, consider this code:
for (i <- 0 until model.productArity) yield {
val fieldValue = model.productElement(i)
fieldValue match {
case x: Int => //use integer classifier
case s: String => //use string classifier
case _ =>
}
}
scalaz library hasn't any macro case class introspection by design, but shapeless has
Consider such definitions:
import shapeless._
import shapeless.tag._
import shapeless.labelled._
trait Omit
val omit = tag[Omit]
case class Model(a: Int, b: String, c: String, d: String ## Omit)
Let define following polymorphic function
object classifiers extends Poly1 {
implicit def stringClassifier[K <: Symbol](implicit witness: Witness.Aux[K]) =
at[FieldType[K, String]](value => Set(witness.value.name.toUpperCase))
implicit def intClassifier[K <: Symbol](implicit witness: Witness.Aux[K]) =
at[FieldType[K, Int]](value => {
val name = witness.value.name
Set(name.toUpperCase, name.toLowerCase)
})
implicit def omitClassifier[K, T] =
at[FieldType[K, T ## Omit]](_ => Set.empty[String])
}
Now your modelClassifier could be done as:
def modelClassifier: Classifier[Model] =
m => LabelledGeneric[Model].to(m).map(classifiers).toList.reduce(_ union _)
you can check it via
println(modelClassifier(Model(1, "b", "c", omit("d"))))
Note that Type ## Tag is subtype of Type so model.d still could be used as String everywhere
How do you intend to distinguish between bClassifier and cClassifier? By name? By order of declaration? That does not sound very "smart" or reliable. Consider encoding your intent explicitly instead. Something like this, perhaps:
case class Classifiable[T](data: T, classifier: Classifier[T])
object Classifiable {
def Null[T](data: T) = Classifiable(data, _ => Nil)
}
case class Model(a: Classifiable[Int], b: Classifiable[String], c: Classifiable[String], d: Classifiable[String])
object Model {
def apply(a: Int, b: String, c: String, d: String) =
Model(
Classifiable(a, aClassifier),
Classifiable(b, bClassifier),
Classifiable(c, cClassifier),
Classifiable.Null(d)
)
}
def modelClassifier(m: Model) = m
.productIterator
.collect { case x: Classifiable[_] =>
x.classifier()(x)
}
.reduce(_ ++ _)
println(modelClassifier(Model(1,"b", "c", "d")))

Can't convert MappedProjection to ProvenShape due to ambiguous implicit

Could you please explain to me how I can convert MappedProjection to ProvenShape which currently fails due to ambiguous implicit?
I use slick-pg for support of jsonb types in Postgres DB.
I have simple case class that I want to store as json and there is ID column with value from case class.
case class Topic(id: String, title: String)
class TopicRow(tag: Tag) extends Table[(String, Topic)](tag, Topic.tableName) {
def id = column[String]("id", O.PrimaryKey)
def json = column[Topic]("json")
def * = (id, json)
}
earlier in the code I have conversion between json and case class
import spray.json._
implicit val TopicColumnType = MappedColumnType.base[Topic, JsValue ](
{ obj => obj.toJson }, { json => json.convertTo[Topic] }
)
What I don't like about TopicRow is that it maps to tuple. I'd like it to be something like this
class TopicRow(tag: Tag) extends Table[Topic](tag, Topic.tableName) {
def id = column[String]("id", O.PrimaryKey)
def json = column[Topic]("json")
val tupled: ((String, Topic)) => (Topic) = tu => tu._2
val unapply: (Topic) => Option[(String, Topic)] = t => Option((t.id, t))
def * = (id, json) <> (tupled, unapply)
}
It fails to compile with error
Error:(43, 22) type mismatch;
found : slick.lifted.MappedProjection[co.younetworks.medstream.server.models.db.Topic,(String, co.younetworks.medstream.server.models.db.Topic)]
required: slick.lifted.ProvenShape[co.younetworks.medstream.server.models.db.Topic]
So I specified explicitly every step of conversion like this
def * = {
val shape: ProvenShape[Topic] = {
val tupled: ((String, Topic)) => (Topic) = tu => tu._2
val unapply: (Topic) => Option[(String, Topic)] = t => Option((t.id, t))
val toToShapedValue: ToShapedValue[(Rep[String], Rep[Topic])] = anyToToShapedValue((id, json))
val mappedProjection: MappedProjection[Topic, (String, Topic)] = toToShapedValue <>(tupled, unapply)
ProvenShape.proveShapeOf(mappedProjection)
}
shape
}
that gives me error
Error:(52, 31) ambiguous implicit values:
both method mappedProjectionShape in object MappedProjection of type [Level >: slick.lifted.FlatShapeLevel <: slick.lifted.ShapeLevel, T, P]=> slick.lifted.Shape[Level,slick.lifted.MappedProjection[T,P],T,slick.lifted.MappedProjection[T,P]]
and method repColumnShape in trait RepShapeImplicits of type [T, Level <: slick.lifted.ShapeLevel](implicit evidence$1: slick.ast.BaseTypedType[T])slick.lifted.Shape[Level,slick.lifted.Rep[T],T,slick.lifted.Rep[T]]
match expected type slick.lifted.Shape[_ <: slick.lifted.FlatShapeLevel, slick.lifted.MappedProjection[co.younetworks.medstream.server.models.db.Topic,(String, co.younetworks.medstream.server.models.db.Topic)], U, _]
ProvenShape.proveShapeOf(mappedProjection)
^
I suspect I confused compiler with presence of mapped column but I don't know how to fix it.
Well, explicit specification of implicit helped. It doesn't look pretty though.
class TopicRow(tag: Tag) extends Table[Topic](tag, Topic.tableName) with JsonMarshallers {
def id = column[String]("id", O.PrimaryKey)
def json = column[Topic]("json")
private val fromTuple : ((String, Topic)) => (Topic) = { case (_, value) => value }
private val toTuple = (v : Topic) => Option((v.id, v))
def * = ProvenShape.proveShapeOf((id, json) <> (fromTuple, toTuple))(MappedProjection.mappedProjectionShape)
}
If anyone knows how to make it shorted please let me know.

Scala - Resolving Templated Type

I have a templated class, and depending on the type of T, I want to print something different.
class Clazz[T](fn: T => String) {}
Ideally, I would like to do something like pattern match T (which I can't do):
T match {
case x:Int => println("function from int to string")
case _ => //... and so forth
}
I tried:
class Clazz[T](fn: T => String) {
def resolve():String = fn match {
case f:(Int => String) => println("function from int to string")
case _ => //...etc.
}
}
and the error message I got was that:
non-variable type argument Int in type pattern Int => String is unchecked since it is eliminated by erasure
Is there a way to work around this?
I would use TypeTag so I can compare T to other types without losing any type info:
import scala.reflect.runtime.universe._
class Clazz[T: TypeTag](fn: T => String) {
val resolve = typeOf[T] match {
case t if t =:= typeOf[Int] => "function from Int to String"
case t if t =:= typeOf[Byte] => ..
}
}
But if I just wanted to print out something different from toString, I would go with a type class:
trait Print[T] {
def toText(t: T): String
def apply(t: T) = print(toText(t))
}
def print[T: Print](t: T) = implicitly[Print[T]].apply(t)
implicit object PrintInt extends Print[Int] {
def toText(t: Int) = s"Int: $t"
}
implicit object PrintByte extends Print[Byte] {
def toText(t: Byte) = s"Byte: $t"
}
PrintInt(3) //=> Int: 3
print(3) //=> Int: 3
3.print is even possible if you add this:
implicit class Printable[T:Print](t: T) {
def print = implicitly[Print[T]].apply(t)
}
3.print //=> Int: 3

How to get manifest in the pattern matching

I want to the get the manifest of one List's inner type like following and pass it to another function, how can I do that ? Thanks
def f(any: Any) = any match {
case x: Int => println("Int")
case a: List[_] => // get the manifest of List's inner type, and use it in the function g()
}
def g[T:Manifest](list:List[T]) = {}
Add the manifest as an implicit requirement to your method, and tweak the type signature a tiny bit:
def f[T](any: T)(implicit mf: Manifest[T]) = mf match {
case m if m == Manifest[Int] => println("Int")
case m if m == Manifest[List[Int]] => println("List of Ints")
//etc...
}
The Manifest class has a method, typeArguments, that should serve your purpose for finding the "inner type". For example
manifest[List[Int]].typeArguments == List(manifest[Int])
You could tweak #Dylan's answer a bit and try this as well:
object ManifestTest {
def f[T](t: T)(implicit m:Manifest[T]) = t match {
case x: Int => println("Int")
case a: List[Any] =>
val innerType = m.typeArguments.head
println(innerType)
}
def main(args: Array[String]) {
f(1)
f(List("hello", "world"))
f(List(1))
}
}