get the class of "case class" from it's companion - scala

I have the following code:
trait Base[A,B] {
def name: String
}
trait BaseCompanion[A,B] {
def classOfBase: Class[_ <: Base[A,B]] // Can I implement something generic here ?
}
case class First(name: String) extends Base[Int,String]
object First extends BaseCompanion[Int,String] {
override def classOfBase: Class[_ <: Base[Int, String]] = classOf[First] // Can this part be generic ?
}
I don't want to override the classOfBase method in every concrete class that will extend BaseCompanion.This can be achieved by changing BaseCompanion to:
abstract class BaseCompanion[A,B, CLAZZ <: Base[A,B] : ClassTag] {
def classOfBase: Class[CLAZZ] = classTag[CLAZZ].runtimeClass.asInstanceOf[Class[CLAZZ]]
}
object First extends BaseCompanion[Int,String,First]
But I don't really like this solution, is there a way to do this without changing the signature of BaseCompanion and implement something generic inside the it ?
By the way today Companion object of any case class "defines" apply(...) method. Given the example above there will be a method similar to this:
abstract class BaseCompanion[A,B, CLAZZ <: Base[A,B] : ClassTag] {
def classOfBase: Class[CLAZZ] = classTag[CLAZZ].runtimeClass.asInstanceOf[Class[CLAZZ]]
/* No need to implement in the companion of a case class that extends Base */
def apply(name: String): Base[A,B]
}
The return type of this apply method is known to the Companion perhaps there is a way to use this information.

Yes, it can be done with this change:
def classOfBase = this.getClass.getMethods.find(_.getName == "apply").get.
getReturnType.asInstanceOf[Class[_ <: Base[A,B]]]
Note that this assumes there is precisely one apply method. In practice, you should check this assumption.

Assuming that if what you want would work, the following would be possible:
case class First(name: String) extends Base[Int, String]
object First extends BaseCompanion[Int, String]
assert(First.baseClass == classOf[First])
Now if that would work, what would stop you from doing the following?
class Second extends BaseCompanion[Int, String]
val second = new Second
println(second.baseClass)
or
// case class Third not defined
object Third extends BaseCompanion[Int, String]
println(Third.baseClass)
What would that result in? Second and Third are not a companion object with an associated class. Second is a class itself!
The problem is that you cannot force your BaseCompanion trait to only be inherited by something that is a companion object. BaseCompanion therefore cannot use the special relation that companion objects and associated classes have. If you want information about the associated class with a companion object, you have to give BaseCompanion that information manually in the definition of your companion object.
The Scala compiler won't allow you to do this. You can work around this with reflection if you want, but whatever solution remains that accomplishes this has to take into account that you are effectively creating unpredictable runtime behaviour. In my opinion, it's best to just help the compiler figure it out, and supply the proper information at compile time.

Related

Prohibit generating of apply for case class

I'm writing a type-safe code and want to replace apply() generated for case classes with my own implementation. Here it is:
import shapeless._
sealed trait Data
case object Remote extends Data
case object Local extends Data
case class SomeClass(){
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}
val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good
I want to prohibit val t: SomeClass = SomeClass() from compiling. Is it possible to do somehow except do not SomeClass to be case class?
There is a solution that is usually used if you want to provide some smart constructor and the default one would break your invariants. To make sure that only you can create the instance you should:
prevent using apply
prevent using new
prevent using .copy
prevent extending class where a child could call the constructor
This is achieved by this interesing patten:
sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
def apply(value: String) = {
// checking invariants and stuff
new MyCaseClass(value) {}
}
}
Here:
abstract prevents generation of .copy and apply
sealed prevents extending this class (final wouldn't allow abstract)
private constructor prevents using new
While it doesn't look pretty it's pretty much bullet proof.
As #LuisMiguelMejíaSuárez pointed out this is not necessary in your exact case, but in general that could be used to deal with edge cases of case class with a smart constructor.
UPDATE:
In Scala 3 you only need to do
case class MyCaseClass private (value: String)
and it will prevent usage of: apply, new and copy from outside of this class and its companion.
This behavior was ported to Scala 2.13 with option -Xsource:3 enabled. You have to use at least 2.13.2 as in 2.13.1 this flag doesn't fix the issue.
So you can make the constructor private and ensure that T is also something different to Nothing.
I believe the best way to ensure the constructor is private (as well as many other things as #MateuszKubuszok show) is to use a (sealed) trait instead of a class:
(if you can not use a trait for whatever reasons, please refer to Mateusz's answer)
import shapeless._
sealed trait Data
final case object Remote extends Data
final case object Local extends Data
sealed trait SomeClass {
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev1: TT =:!= Data, ev2: TT =:!= Nothing): Aux[TT] =
new SomeClass { override final type T = TT }
}
Which works like this:
SomeClass() // Does not compile.
SomeClass.apply[Remote.type] // Compiles.
SomeClass.apply[Data] // Does not compile.
You can see it running here.
If you want to prohibit using some of auto-generated methods of a case class you can define the methods (with proper signature) manually (then they will not be generated) and make them private (or private[this]).
Try
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
private def apply(): SomeClass = ??? // added
}
val t: SomeClass = SomeClass() // doesn't compile
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //doesn't compile
In principle, the methods (apply, unapply, copy, hashCode, toString) can be generated not by compiler itself but with macro annotations. Then you can choose any subset of them and modify their generation as you want.
Generate apply methods creating a class
how to efficiently/cleanly override a copy method
Also the methods can be generated using Shapeless case classes a la carte. Then you can switch on/off the methods as desired too.
https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/alacarte.scala
https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/alacarte.scala

How to make type classes resolve for base/abstract class?

Say I've a base abstract class and 2 case classes extending it.
sealed abstract class Base extends Product with Serializable
case class A(d: String) extends Base
case class B(d: Int) extends Base
And I have also a type class on A and B, for example
trait Show[T] {
def show(t: T): String
}
object Show {
def apply[T](t: T)(implicit show: Show[T]): String = show.show(t)
implicit val showA: Show[A] = new Show[A] {
def show(t: A): String = "A"
}
implicit val showB: Show[B] = new Show[B] {
def show(t: B): String = "B"
}
}
The problem I have is that, in my code I get A and B from deserialization and they have type Base. In this case scala fail to resolve the typeclasses because there's not type classes defined on Base.
I could solve this problem by defining an instance on Base and do a pattern match but IMO in this way we'd better not use typeclasses at all.
Is there any tricks that we can make scala resolve type classes for a base class?
Thanks.
No, there is no such trick. Type has to be statically known because it is the compiler who resolves the typeclass instance.
As you said an instance for Base that will resolve proper instance has to be created. It can be done manually but there might be better ways to do it. Maybe someone can provide a better answer on how to do it nicely, but fundamentally the instance for Base is needed.

Can companion object extend some trait different from companion class?

I've always used companion objects just as they should be: having some class or trait, I've defined the object with the same class name in the same file.
But I'm trying at the moment to factor out some common functionality that few companion objects share and wonder if it is safe to have something like this:
trait Label {
def label: String
}
trait InstancesMap[T <: Label] {
private var instances = Map.empty[String, T]
def init(instance: T): T = {
instances += (instance.label -> instance)
instance
}
def byLabel(label: String): T = instances(label)
}
case class EventStatus(label: String) extends Label
object EventStatus extends InstancesMap[EventStatus] {
val DRAFT = init(EventStatus("draft"))
val PUBLISHED = init(EventStatus("published"))
}
I am not sure if it is safe for case class companion object to extend some other trait. It compiles and works fine, but would be great to hear some opinions.
Of course it can, just like non-companion objects. That's actually one of the major advantages of representing "statics" as companion objects. Extending a class instead of a trait works too.
You can see it in the standard library where collection companion objects extend GenericCompanion and its various subtypes.

Return class instance using generic inside companion object for method specified in trait

In Scala I want to return a instance of a class for a method defined in a trait which uses generics, the code example I have is this:
File 1
package packOne
import packTwo.A
trait MyTrait[T <: MyTrait[T <: A]] {
def otherFunct(): String
def funct[T <: A](): T
}
File 2
package packTwo
import packOne.MyTrait
abstract class A(someParameter: String) {}
class B(someParameter: String) extends A(someParameter) {}
object B extends MyTrait[B] { // <--- the B inside MyTrait here is the class not the object, or at least that is what I want
def otherFunct(): String = "Hello"
def funct[B](): C = new B("hi") // <--- I think here is the key
}
basically what I want is an interface that have method to return a concrete implementation of class A, in an implementing object (which happen to be a companion object for a class extending A).
Why do I want that to be on an object?, is because I want to call that method without the need of an instance (like an static method in java), so that I can call B.funct() and have an instance of B class kind of like a factory method, for other classes extending A for example a call to X.funct will return an instance of class X.
I have tried to remove the generic type from the function definition except on the return type of the function and just leave it in the trait definition (like def funct(): T) but that does not work either.
I am quite new to Scala so if you could explain it for dummies and avoid complex scala unique concepts I would appreciate
How about simply:
trait A
class B(someParameter: String) extends A
trait MyTrait[T <: A] {
def otherFunct: String //Parentheses on parameterless methods with no side effects and no serious computation are generally unidiomatic in Scala
def funct: T //Note, no generic parameter on this method
}
object B extends MyTrait[B] {
def otherFunct = "Hello"
def funct = new B("hi")
}
And then:
B.funct //returns a new `B`
The apply method is often used in this factory style (e.g. Seq.apply() which is equivalent to Seq())

Scala: lock extended class

I'd like to "lock" a class, which is extended from a trait. Is it possible in Scala?
For example I have:
trait A {
val boris: String
val john: String
val number: Int
}
class B extends A {
// do something with these values
}
but can I ensure, that in class B no new values will be added if those aren't declared in trait A?
Thanks for your answers.
You cannot.
But if you simply mark the trait as sealed and provide a default implementation:
sealed trait A { val boris: String }
final class B(val boris: String) extends A {}
then people are free to create implicit value classes that make it look like new functionality has been added (except without actually creating the class):
implicit class MyB(val underlying: B) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B("fish")).sirob // "hsif"
You can also let the classes take a type parameter as a marker if you want to keep them straight at compile-time (though not runtime):
sealed trait A[T] { val boris: String }
final class B[T](val boris: String) extends A[T] {}
implicit class MyB(val underlying: B[Int]) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B[Int]("fish")).sirob // "hsif"
(new B[Char]("fish")).sirob // error: value sirob is not a member of B[Char]
So you could--especially with 2.10--simply lock everything and let users enrich the original interface this way.
I'm not sure if this covers your intended use case, though; it doesn't provide any inheritance.
Based on your example and my guess at what you are actually trying to do, you may want to consider just using case classes.
Extending a case class is generally avoided (I think it will spit out deprecation warnings if you try), so that will prevent people from wanting to extend your class in order to add functionality.
Translating your example into a case class:
case class A (boris: String, john: String, number: Int)
Then instead of extending A to change its values, you'd just make a new instance, e.g.
val a2 = someOtherA.copy(john="Doe")