How to inherit generic case class to reuse its properties? - scala

How to properly implement this in Scala?
case class WithPagingJson[T](meta: PagingMetaJson, objects: Array[T])
case class CandidatesListJson extends WithPagingJson[CandidateJson]
case class OpeningsListJson extends WithPagingJson[OpeningJson]
As you see, these classes are models for JSON data received from various APIs.
They all have same basic structure with two top-level properties for paging info and for array of requested objects.
Compiler tells me case to case inheritance is prohibited.
What is a proper Scala way of doing this?

If CandidatesJson really doesn't add anything to WithPagingJson, it can be a type alias:
type CandidatesJson = WithPagingJson[CandidateJson]
Otherwise, does WithPagingJson really need to be a case class? If you make it abstract and all its subtypes case classes, then you effectively still get all the equality, hash code, etc. benefits.
Would you mind showing the code for inheriting from abstract class?
abstract class WithPagingJson[T](val meta: PagingMetaJson, val objects: Array[T])
case class CandidatesJson(override val meta: PagingMetaJson, override val objects: Array[CandidateJson]) extends WithPagingJson[CandidateJson](meta, objects)
it seems I will have to exactly repeat whole parameters list for case class anyways? I actually was hoping to avoid exactly that.
If they are the same, yes, but they don't have to be. Maybe you want to make the base type a trait instead
trait class WithPagingJson[T] {
val meta: PagingMetaJson
val objects: Array[T]
}
case class CandidatesJson(meta: PagingMetaJson, objects: Array[CandidateJson]) extends WithPagingJson[CandidateJson]

Related

Scala: force type parameter to be a case class

I have an abstract class Model from which I create case classes:
abstract class Model
case class User(.) extends Model
an abstract class Table taking such a Model as type parameter, used in one of its default concrete methods:
abstract class Table[M <: Model] {
def parser = SomeExternalBuilder[M]
}
The meaning is rather simple: "Give every instance of Table a default parser based on its own class".
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
Can I make Table take only case classes as type parameter?
I have seen a few answers providing a missing copy method (ref1, ref2), so I tried this:
trait Model[T] {
def copy: T
}
abstract class Table[M <: Model[M]]
but now case class User extends Model[User] and must overwrite copy too, every function creating a Model takes a type parameter, and honestly the code quickly starts being atrocious, all that for that single line in Table.
Is there no better way than copying that def parser line in every child's body?
Edit: N.B. The real function is def parser: anorm.Macro.namedParser[M] from the "anorm" library for Play.
Edit: Source of the type check by this macro: https://github.com/playframework/anorm/blob/0a1b19055ba3e3749044ad8a54a6b2326235f7c8/core/src/main/scala/anorm/Macro.scala#L117
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
I don't think you can ever get such a message from Scala compiler itself, which means that SomeExternalBuilder.apply is a macro. It requires a specific case class in order to know its fields, so that it doesn't matter if you could limit M to be a case class (which you can't): it still wouldn't accept a type parameter.
What you can do is create a macro annotation, so that when you write e.g.
#HasModel
class SomeTable extends Table[SomeModel] {
...
}
the val parser = namedParser[SomeModel] is generated automatically.
Alternately, write #HasModel[SomeModel] class SomeTable { ... } and generate extends Table[SomeModel] as well.
It wouldn't be hard (as macros go), but you still need to annotate each class extending Table.
Not fool proof solution but worth a try
case classes extend Product and Serialisable. Constraint Product with Serialisable will help you get some type safety. M can be any class which extends Product with Serialisable. But Product is extended by case class mostly
abstract class Table[M <: (Product with Serializable)] {
def parser = SomeExternalBuilder[M]
}

scala value class multiple inheritance

I have in my project objects that represent IDs.
Let's say it is ChairId, TableId, LampId. I want them all to inherit from GenericId. And I want to be able to call def f(x: GenericId) = x.id
I want them to hold only single id: String so I would like to make them extend AnyVal.
Also I would like for each type to provide function generate which would generate my specific ID i.e. I would like to type something like ChairId.generate()
I have typed this:
sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id
And I though if GenericId would inherit from AnyVal that would work but so far no luck ;/ I also tried making GenericId a trait and make case classes extend AnyVal with GenericId but also won't compile :/
Another thing with TableId.generate() I can provide companion object just with function generate and that basically solve my problem but I wondered if there is possibility to solve that without defining companion object? (i.e. through implicits somehow)
// edit
regarding comment to provide code which doesn't compile(and I would like to):
sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)
Value classes cannot work this way for a couple of reasons.
First, from the documentation, value classes cannot be extended by any other class, so AbstractId cannot extend AnyVal. (Limitation #7)
scala> abstract class AbstractId(val id: String) extends AnyVal
<console>:10: error: `abstract' modifier cannot be used with value classes
abstract class AbstractId(val id: String) extends AnyVal
^
Second, even if you make AbstractId a trait, and define the other ids like this:
final case class DogId(val id: String) extends AnyVal with AbstractId
.. the usage of the value class wouldn't fit your case, because the class itself would still get allocated. See the allocation summary:
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
Some quotes from the value classes SIP that are likely to clarify your doubts:
Value classes...
...must have only a primary constructor with exactly one public, val
parameter whose type is not a value class.
... cannot be extended by another class.
As per 1. it can not be abstract; per 2. your encoding doesn't work.
There is another caveat:
A value class can only extend universal traits and cannot be extended
itself. A universal trait is a trait that extends Any, only has defs
as members, and does no initialization. Universal traits allow basic
inheritance of methods for value classes, but they incur the overhead
of allocation.
With all that in mind, based on your last snippet, this might work:
sealed trait AbstractId extends Any { def id: String }
final case class CatId(id: String) extends AnyVal with AbstractId
final case class DogId(id: String) extends AnyVal with AbstractId
But keep in mind the allocation only occurs if you want to use CatId and DogId as an AbstractId. For better understanding I recommend reading the SIP.

Accessing parameters of the same name of different classes in Scala

I have a very specific scenario, in which I have some different abstract classes the have child case classes that can have different parameters, for example:
abstract class ball() {}
case class football(_name: String, _shape: String) extends ball
case class basketball(_name: String, _size: Int) extends ball
and a different abstract class:
abstract class food() {}
case class vegetarian(_name: String, calories: Int) extends food
case class meat(_name: String, proteinCount: Int) extends food
Now, the problem I'm facing is that I need to somehow extract the name of all of those without knowing what class it is, I just know that ALWAYS, EACH CLASS has a parameters named _name.
Supposing we have an object of any of above classes, I'm trying to do it like this:
object.getClass.getDeclaredField("_name").get(this)
But I'm getting the error:
can not access a member of class package.food with modifiers "private"
I tried putting val and var before parameters in class but it doesnt help. I also tried doing "setAccessible(true)" in a line before get(this), which also doesn't help.
The obvious clean solution would be to have a least a common trait to all these classes:
trait HasName {
def _name: String
}
and then you can safely do obj.asInstanceOf[HasName]._name. Better yet if you manage to keep around the static information that obj is a HasName, in which case obj._name suffices.
If you can't do any of that, reflection is the way to go. You can do it pretty easily using a structural type, in this case:
obj.asInstanceOf[{ def _name: String }]._name
Note that this will be slower than the above HasName solution, and completely unchecked at compile time.

In Scala is there a way to reference the Companion Object from within an instance of a Case Class?

In my specific case I have a (growing) library of case classes with a base trait (TKModel)
Then I have an abstract class (TKModelFactory[T <: TKModel]) which is extended by all companion objects.
So my companion objects all inherently know the type ('T') of "answers" they need to provide as well as the type of objects they "normally" accept for commonly implemented methods. (If I get lazy and cut and paste chunks of code to search and destroy this save my bacon a lot!) I do see warnings on the Internet at large however that any form of CompanionObject.method(caseClassInstance: CaseClass) is rife with "code smell" however. Not sure if they actually apply to Scala or not?
There does not however seem to be any way to declare anything in the abstract case class (TKModel) that would refer to (at runtime) the proper companion object for a particular instance of a case class. This results in my having to write (and edit) a few method calls that I want standard in each and every case class.
case class Track(id: Long, name: String, statusID: Long) extends TKModel
object Track extends TKModelFactory[Track]
How would I write something in TKModel such that new Track(1, "x", 1).someMethod() could actually call Track.objectMethod()
Yes I can write val CO = MyCompanionObject along with something like implicit val CO: ??? in the TKModel abstract class and make all the calls hang off of that value. Trying to find any incantation that makes the compiler happy for that however seems to be mission impossible. And since I can't declare that I can't reference it in any placeholder methods in the abstract class either.
Is there a more elegant way to simply get a reference to a case classes companion object?
My specific question, as the above has been asked before (but not yet answered it seems), is there a way to handle the inheritance of both the companion object and the case classes and find the reference such that I can code common method calls in the abstract class?
Or is there a completely different and better model?
If you change TKModel a bit, you can do
abstract class TKModel[T <: TKModel] {
...
def companion: TKModelFactory[T]
def someMethod() = companion.objectMethod()
}
case class Track(id: Long, name: String, statusID: Long) extends TKModel[Track] {
def companion = Track
}
object Track extends TKModelFactory[Track] {
def objectMethod() = ...
}
This way you do need to implement companion in each class. You can avoid this by implementing companion using reflection, something like (untested)
lazy val companion: TKModelFactory[T] = {
Class.forName(getClass.getName + "$").getField("MODULE$").
get(null).asInstanceOf[TKModelFactory[T]]
}
val is to avoid repeated reflection calls.
A companion object does not have access to the instance, but there is no reason the case class can't have a method that calls the companion object.
case class Data(value: Int) {
def add(data: Data) = Data.add(this,data)
}
object Data {
def add(d1: Data, d2: Data): Data = Data(d1.value + d2.value)
}
It's difficult. However you can create an implicit method in companion object. whenever you want to invoke your logic from instance, just trigger implicit rules and the implicit method will instantiate another class which will invoke whatever logic you desired.
I believe it's also possible to do this in generic ways.
You can implement this syntax as an extension method by defining an implicit class in the top-level abstract class that the companion objects extend:
abstract class TKModelFactory[T <: TKModel] {
def objectMethod(t: T)
implicit class Syntax(t: T) {
def someMethod() = objectMethod(t)
}
}
A call to new Track(1, "x", 1).someMethod() will then be equivalent to Track.objectMethod(new Track(1, "x", 1)).

scala case object pollution

Let me explain ;-)
both classes below are in package com.company.foo
RoleGroup.scala
abstract class RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends RoleGroup
MailSender.scala
abstract class MailSender
case object ADMIN extends MailSender
case object STAFF extends MailSender
case object ACCOUNTANT extends MailSender
The problem here is that ADMIN is ambiguous since there is no namespace separation with case objects. It seems there can only be one uniquely named case object per package.
I suppose I could name case objects based on function a la mailADMIN, roleADMIN and so on. Or should I just create proper Enums and forget about case objects? Or take some other approach?
you could do something like:
object RoleGroup {
sealed trait RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends Rolegroup
}
and similarly for MailSender. Then in situations where you're only using one, you can do import MailSender._ or vise versa, but when using both you refer to them as RoleGroup.ADMIN, etc.
Whether you want to take this approach or use Enums mostly depends on how you intend to use them. In this aproach, each "enum" is a type, whereas with Enums each enum is a value. The former works better for pattern matching since the compiler can check if your matches are exhaustive, the latter is better (IMO) for working with serialization.
You don't need enums here, as much as probably anywhere else. All you need is proper namespacing. I find the companion object approach to be the most benefitial:
sealed abstract class RoleGroup
object RoleGroup {
case object Admin extends RoleGroup
case object Member extends RoleGroup
// also just in case
sealed case class SomeParameterizedGroup (
param1: Any,
param2: Int
) extends RoleGroup
}
Using it this way will very much remind you of Java's enums, while also providing you with strengths of Scala:
def foo (roleGroup: RoleGroup) =
roleGroup match {
case RoleGroup.Admin =>
case RoleGroup.SomeParameterizedGroup(param1, param2) =>
}
Please note that in Dan's solution the type of roleGroup would be RoleGroup.RoleGroup, which kinda feels unnatural. Also note that uppercase names violate Scala's style conventions and from examples above you can kinda see why.