Return Generic Polymorphic Return Type in Scala - scala

In an example below, I like to return any type of Container from the function getInfos and from the caller side to perform map with pattern matching however I get compiler error that I cannot return Container[_]. Is there a way to return Container of any type? If not, what is the best way to approach this?
trait Info[T] {
def value: T
}
case class ContainerAInfo(value: Long) extends Info[Long]
case class ContainerBInfo(value: String, value2: String) extends Info[String]
trait Container[T] {
def info: Info[T]
}
case class ContainerA[Long](projectionInfo: ContainerAInfo)
case class ContainerB[String](projectionInfo: ContainerBInfo)
def getInfos: Seq[Container[_]] = {
Seq(
ContainerA(
projectionInfo = ContainerAInfo(1L)
),
ContainerB(
projectionInfo = ContainerBInfo("12", "12")
)
)
}

Long and String in
case class ContainerA[Long](...)
case class ContainerB[String](...)
are not standard Long (scala.Long) and String (scala.Predef.String aka java.lang.String). You introduce new generics shadowing standard Long and String. It's better to switch on scalacOptions += "-Xlint:type-parameter-shadow" to avoid such confusion
Type Arguments and Bounds in Scala
What causes this type error in a pattern guard for matching list of tuples
Strange Error with String in Scala 2.12.7
Type parameters applied to Scala Function
I suspect ContainerA and ContainerB are supposed to extend Container, don't they? So a minimal change to your code making it compile is
case class ContainerA(info: ContainerAInfo) extends Container[Long]
case class ContainerB(info: ContainerBInfo) extends Container[String]
def getInfos: Seq[Container[_]] = {
Seq(
ContainerA(
info = ContainerAInfo(1L)
),
ContainerB(
info = ContainerBInfo("12", "12")
)
)
}
Container[_] is an existential type, a minimal supertype of all Container[T], including Container[Long], Container[String], Container[Any]
scala - Any vs underscore in generics
Understanding scala's _ vs Any/Nothing
What is an existential type?

You can consider making the hierarchy covariant and using Seq[Container[Any]]:
trait Info [+T] {
def value: T
}
case class ContainerAInfo (value: Long) extends Info [Long]
case class ContainerBInfo (value: String, value2: String) extends Info [String]
trait Container [+T] {
def projectionInfo: Info [T]
}
case class ContainerA (projectionInfo: ContainerAInfo) extends Container [Long]
case class ContainerB (projectionInfo: ContainerBInfo) extends Container [String]
def getInfos: Seq [Container [Any]] =
Seq (
ContainerA (
projectionInfo = ContainerAInfo (1L)
), ContainerB (
projectionInfo = ContainerBInfo ("12", "12")
)
)
Though possibly it will not be that useful.
Demo

Related

assign any val scala pureconfig during configuration read

I know this is going against the very nature of Scala pureconfig ... however ...
Is it even feasible to implement with scala pureconfig configuration reading for this case class, so that instead of having strongly typed value( as String) for the constructor parameter "variable" to have Any type or at least String, Integer, Double, Array[Strings], Array[Integer], Array[Double].
case class Filter(
field: String,
operator: String,
variable: String // should support Int , Double , List[String], List[Int]
)
To my poor understanding, neither CoProductHint nor Custom Reader approach will work ...
By default pureconfig doesn't provide a way to read Any. If for a specific class you would like to read Any then you can define a codec for Any in the context of that class:
case class Filter(field: String, operator: String, variable: Any)
implicit val readFilter = {
implicit val readAny = new ConfigReader[Any] {
def from(config: ConfigValue): Either[ConfigReaderFailures, Any] = {
Right(config.unwrapped())
}
}
ConfigReader[Filter]
}
and then you can read Filter
val config = ConfigFactory.parseString(
"""
{
field: "foo"
operator: "bar"
variable: []
}
""")
println(pureconfig.loadConfig[Filter](config))
// will print Right(Filter(foo,bar,[]))
unwrapped converts a ConfigValue to Any recursively.
So the answer is yes, it if possible to tell pureconfig how to read Any.
The reason why pureconfig doesn't provide the codec for Any by default is because Any is the ancestor of all the classes in Scala and it's impossible to create a codec for anything (e.g. database connections). When you know that you are expecting a restricted set of types, like the ones you listed, you can wrap everything in a coproduct:
sealed abstract class MySupportedType
final case class MyInt(value: Int) extends MySupportedType
final case class MyDouble(value: Double) extends MySupportedType
final case class MyListOfString(value: List[String]) extends MySupportedType
final case class MyListOfInt(value: List[Int]) extends MySupportedType
final case class Filter2(field: String, operator: String, variable: MySupportedType)
and then use the default way to extract the coproduct value or a custom codec for MySupportedType
val config = ConfigFactory.parseString(
"""
{
field: "foo"
operator: "bar"
variable: {
type: mylistofint
value: []
}
}
""")
println(pureconfig.loadConfig[Filter2](config))
// will print Right(Filter2(foo,bar,MyListOfInt(List())))
Using a coproduct instead of Any limits the possible values that variable can have and let the compiler help you if something is wrong with what you are doing.
You can make that field ANY:
Example:
scala> case class Filter(
| field: String,
| operator: String,
| variable: Any // should support Int , Double , List[String], List[Int]
| )
defined class Filter
scala> Filter("anurag","data",List(12,34))
res5: Filter = Filter(anurag,data,List(12, 34))
scala> Filter("anurag","data",List(12.12,34.12))
res6: Filter = Filter(anurag,data,List(12.12, 34.12))
scala> Filter("anurag","data",List("Hello","Hii"))
res8: Filter = Filter(anurag,data,List(Hello, Hii))

Scala generics in function return value

I have these classes defined.
trait ResultTrait {
}
case class PostResult (
#Key("_id") id: String,
success: String,
errors: Seq[String] = Seq.empty
) extends ResultTrait
case class PostError (
message: String,
errorCode: String
) extends ResultTrait
This won't compile. It gives error "Required T, but found PostResult (or PostError)".
def postLead[T <: SFDCResult](accessToken: AccessToken):
Future[T] = {
// depends on response from request, return PostResult or PostError
}
As #Travis Brown has already stated, it looks like you're trying to express the variability of the return type (i.e. "it's either a PostResult or a PostError") through generics, when really all you need is the parent trait.
Assuming your SDFCResult was an anonymization error where you meant to use ResultTrait, I would use the following:
// Make the trait sealed so we can only have our two known implementations:
sealed trait ResultTrait {}
...
// Two subclasses as before
And then your method should just be:
def postLead(accessToken: AccessToken):Future[ResultTrait] = {
// depends on response from request, return PostResult or PostError
}

Type safety when optional field is guaranteed to be present

Let's say I have a following case class:
case class Product(name: String, categoryId: Option[Long]/*, other fields....*/)
Here you can see that categoryId is optional.
Now let's say I have a following method in my DAO layer:
getCategoryProducts(): List[Product] = {
// query products that have categoryId defined
}
You see, that this method returns products, that are guaranteed to have categoryId defined with some value.
What I would like to do is something like this:
trait HasCategory {
def categoryId_!: Long
}
// and then specify in method signature
getCategoryProducts(): List[Product with HasCategory]
This will work, but then such a product will have two methods: categoryId_! and categoryId that smells bad.
Another way would be:
sealed trait Product {
def name: String
/*other fields*/
}
case class SimpleProduct(name: String, /*, other fields....*/) extends Product
case class ProductWithCategory(name: String, categoryId: Long/*, other fields....*/) extends Product
def getCategoryProducts: List[ProductWithCategory] = ...
This method helps to avoid duplicate methods categoryId and categoryId_!, but it requires you to create two case classes and a trait duplicating all the fields, which also smells.
My question: how can I use Scala type system to declare this specific case without these fields duplications ?
Not sure how much this will scale for your particular case, but one solution that comes to mind is to parameterize over the Option type using a higher-kinded generic type:
object Example {
import scala.language.higherKinds
type Id[A] = A
case class Product[C[_]](name: String, category: C[Long])
def productsWithoutCategories: List[Product[Option]] = ???
def productsWithCategories: List[Product[Id]] = ???
}
A way to do it is to use type classes -
import scala.language.implicitConversions
object Example {
sealed class CartId[T]
implicit object CartIdSomeWitness extends CartId[Some[Long]]
implicit object CartIdNoneWitness extends CartId[None.type]
implicit object CartIdPresentWitness extends CartId[Long]
case class Product[T: CartId](name: String, categoryId: T /*, other fields....*/)
val id: Long = 7
val withId = Product("dsds", id)
val withSomeId = Product("dsds", Some(id))
val withNoneId = Product("dsds", None)
val presentId: Long = withId.categoryId
val maybeId: Some[Long] = withSomeId.categoryId
val noneId: None.type = withNoneId.categoryId
val p = Product("sasa", true) //Error:(30, 18) could not find implicit value for evidence parameter of type com.novak.Program.CartId[Boolean]
}
This solution involves some code and dependent on implicits but does what you're trying to achieve.
Be aware that this solution is not completely sealed and can be 'hacked'. You can cheat and do something like -
val hack: Product[Boolean] = Product("a", true)(new CartId[Boolean])
val b: Boolean =hack.categoryId
For some more - advanced solutions which include
* Miles Sabin (#milessabin)’s Unboxed union types in Scala via the Curry-Howard isomorphism
* Scalaz / operator
http://eed3si9n.com/learning-scalaz/Coproducts.html

Macro to map "items" to their type parameterized "containers" -- large compiler error

Suppose I have a trait for "items" and another trait for "containers" of those items:
sealed trait Item
case class Marble() extends Item
case class Book() extends Item
sealed trait Container[Item]
case object Pouch extends Container[Marble]
case object Shelf extends Container[Book]
Using the type information above, I'd like to construct a map like so via a macro and some runtime reflection:
// Map(classOf[Marble] -> Pouch, classOf[Book] -> Shelf)
val itemToContainer = typeParamMap[Container[_], Item](Pouch, Shelf)
Here's my attempt, which compiles fine, and works in an intermediate form that just returns the list of TypeTags:
object Macros {
def getTag[T](implicit ttag: ru.TypeTag[T]) = ttag
def getBaseParam(child: ru.Type, base: ru.Symbol, paramBase: ru.Symbol) =
child.baseType(base)
.find(_.baseClasses.contains(paramBase))
.get
.typeSymbol
.asClass // TODO how to get java.lang.Class from here?
def typeParamMapImpl[B, P](c: Context)(children: c.Expr[B]*)(baseTag: c.Expr[ru.WeakTypeTag[B]], paramTag: c.Expr[ru.WeakTypeTag[P]]) = {
import c.universe._
val getTagTree = Select(Ident(newTermName("Macros")), "getTag")
val typeTags = c.Expr[List[ru.TypeTag[_]]](Apply(reify(List).tree, children.map( t =>
c.Expr(TypeApply(getTagTree, List(t.tree))).tree
).toList))
reify {
typeTags.splice.map { child =>
getBaseParam(child.tpe, baseTag.splice.tpe.typeSymbol, paramTag.splice.tpe.typeSymbol) -> child.tpe
}.toMap
}
}
def typeParamMap[B, P](children: B*)(implicit baseTag: ru.WeakTypeTag[B], paramTag: ru.WeakTypeTag[P]) = macro Macros.typeParamMapImpl[B, P]
}
However when I try to compile with an invocation of this, I get a huge compiler error: https://gist.github.com/4647812
Can anyone spot the problem, or suggest a better way of going about this?
Thanks!

Scala child case class param name conflict with parent case class param name

Let us assume we have the two following classes:
abstract case class MyParent(param: Int) {
// ...
}
case class MyChild(param: Int) extends MyParent(param: Int) {
// ... ^^^^^ ^^^^^
}
Making them both case classes resulted in an error in both param usage places, which says that it needs the override modifier that will override the value from the parent class. This looks strange to me.. why do I have to invent other param names here.. why is this order of things enforced? Where is the profit?
You never should derive a case class from another case class!
Try this in a REPL started with scala -deprecation:
scala> case class Foo(foo: String)
defined class Foo
scala> case class Bar(override val foo: String) extends Foo(foo)
<console>:9: warning: case class `class Bar' has case ancestor `class Foo'. Case-to-case inheritance has potentially dangerous bugs which are unlikely to be fixed. You are strongly encouraged to instead use extractors to pattern match on non-leaf nodes.
A quick fix for that is to do this
abstract case class MyParent(param: Int) {
println("Form parent : " + param)
}
case class MyChild(override val param: Int) extends MyParent(param) {
println("Form child : " + param)
}
val c = MyChild(10);
This will result in this
>> From parent : 10
>> From child : 10
Actually the extends MyParent() is not like in Java. This is a way to tell MyChild extends MyParent, and call the super constructor of the latter at first.
Where is the profit?
Simply the lack of a not-too-useful special case. param in case class MyChild(param: Int) is a class member as well as a constructor parameter, so one of ancestors already has a (non-abstract) param member, it has to be overridden. And Scala requires override keyword when overriding everywhere else, so it requires it here as well.
As far as my limited Scala knowledge goes, case classes are typically used for immutable algebraic data types and pattern matching. Therefore, instead of creating a "child class", you should probably instead create a class which contains the "parent" class.
> case class MyParent(param: Int)
defined class MyParent
> case class MyChild(param: Int, parent: MyParent)
defined class MyChild
> def foo(c: MyChild) = c match {
case MyChild(p, MyParent(p2)) => println("p: " + p + ", p2 " + p2)
}
foo: (c: MyChild)Unit
> foo(MyChild(3, MyParent(4)))
p: 3, p2 4