Can companion object extend some trait different from companion class? - scala

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.

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

Which among importing companion object or extending trait is better

I have a JSON protocol written in spray
trait MyJsonProtocol {
//some logic
}
object MyJsonProtocol extends MyJsonProtocol {
}
Now which is better ?? Importing this companion object or extending the trait ?
If you are creating some JsonFormat instances for spray, then you can just create an object directly and import that. This means that you only have a single instance of your implicit vals and objects.
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object MyTypeJsonFormat extends RootJsonFormat[MyType] {
def write(v: MyType): JsValue = ...
def read(value: JsValue): MyType = ...
}
implicit val myClassFormat = jsonFormat5(MyClass)
}
class OtherClass {
import MyJsonProtocol._
...
}
It depends on the scenario, Because a companion class or object can access the private members of its companion. Use a companion object for methods and values which are not specific to instances of the companion class. If you just want multiple inheritance but allow code reusability then trait is fine.
Hope it helps.
This depends on your logic. If you define some implicits, importing an object and extending a trait are different. If you import you define implicits of the same priority as local ones. If you extend you create low-priority implicits in comparison with local ones.

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

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.

Can I do structural refinement over a constructor?

In my code I have something like this:
trait MyObj
trait Companion {
type C <: MyObj
}
Then I have several pairs of classes and companion objects, where the companions extend Companion and the classes extend MyObj and define a constructor with a String parameter. I can't just make case classes inheriting from MyObj, cause then I can't make the companions extend Companion, but I end up repeating the same code in every companion:
class Foo(name: String) extends MyObj
object Foo extends Companion {
type C = Foo
def apply(name: String) = new Foo(name)
// new C(name) works too
}
I'd like to move the implementation of apply to the Companion trait:
trait Companion {
type C <: MyObj { def this(name: String) }
def apply(name: String) = new C(name)
}
and now the inheriting objects just need to specify the type of C. But this doesn't compile. Is there any way of telling the compiler that C must have a specific constructor, so that I can call new C, without having to manually resort to reflection?
What about adding ClassTag or TypeTag? It can be some sort of intermediate solution towards reflection:
abstract class Companion[C <: MyObj](implicit ct:ClassTag[C]) {
def apply(name: String):C =
ct.getConstructor(classOf[String]).invoke(Array[Any](name)).asInstanceOf[C]
}
(not sure in the exact syntax)

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")