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.
Related
Given a code which takes a type, takes it's known direct subclasses, filters the ones that are case classes and then takes the companion of that case class:
def firstSubclassWithCompanion[T: TypeTag]: String = {
val superclass = implicitly[TypeTag[T]].tpe.typeSymbol.asClass
val caseClass = superclass.knownDirectSubclasses.map(_.asClass).filter(_.isCaseClass).head
s"case class $caseClass has companion ${caseClass.companion}"
}
With a simple example
sealed trait With
case class WithCase() extends With
It gives the expected return
> firstSubclassWithCompanion[With]
"class WithCase has companion object WithCase"
As With trait has a WithCase subclass, which is case class that has a companion object (defined by compiler).
However, given the following example, where the subclass is defined in the companion object of the inheriting trait:
sealed trait Without
object Without {
case class WithoutCase() extends Without
}
It doesn't return the companion object
> firstSubclassWithCompanion[Without]
"class WithoutCase has companion <none>"
It works fine if it's defined in other object.
Bugs should be reported at https://github.com/scala/bug/issues
A workaround is to use caseClass.owner.typeSignature.decl(caseClass.name) instead of caseClass.companion.
Another workaround is to translate this runtime-reflection code into a macro (compile-time reflection). Since all the classes here are defined at compile time it makes sense to use a macro.
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def firstSubclassWithCompanion[T]: String = macro firstSubclassWithCompanionImpl[T]
def firstSubclassWithCompanionImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val superclass = weakTypeOf[T].typeSymbol.asClass
val caseClass = superclass.knownDirectSubclasses.map(_.asClass).filter(_.isCaseClass).head
val res = s"case class $caseClass has companion ${caseClass.companion}"
q"$res"
}
I am implementing an extension of ml.Transformer in Spark; but this question is Scala specific. Here is an example object (part of a Class/Object pair):
abstract class UDFTransformer(func: UserDefinedFunction,
inputFieldNames: Seq[String],
outputFieldName: String) extends Transformer with MLWritable with Serializable {
... definitions here ...
}
object UDFTransformer extends MLReadable[UDFTransformer] {
// Since there are no parameters associted with the UDF, there is nothing to save!
class Writer(instance: UDFTransformer) extends MLWriter {
override protected def saveImpl(path: String): Unit = {}
}
abstract protected class Reader extends MLReader[UDFTransformer]
override def read: MLReader[UDFTransformer] = new Reader
override def load(path: String): UDFTransformer = super.load(path)
}
The new Reader does not compile because the class is abstract and cannot be instantiated. But; any child class will have to define it; along with its necessary members. I cannot just make read abstract as well, this gives me a warning Only classes can have declared but undefined methods.
The fundamental problem is that each child class of my UDFTransformer is going to wrap a specific UDF. Therefore, the reader needs to be able to generate a specific UDF object; this can't be declared in the superclass. But this 'factory' belongs in the companion object, not in the abstract class itself.
How can I go about building a companion object for an abstract class that can leave the definition of read undefined?
The normal way to do it is by creating an abstract class or trait for the companion objects. Something like
abstract class UDFTransformerCompanion[T <: UDFTransformer] extends MLReadable[T] {
abstract def read: MLReader[T]
override def load(path: String): T = super.load(path)
}
class SomeTransformer extends UDFTransformer { ... }
object SomeTransformer extends UDFTransformerCompanion[SomeTransformer] {
override def read: MLReader[SomeTransformer] = ...
}
Not sure why you have the load = super.load override, and it doesn't look like you can have a companion object for the UDFTransformer itself, at least not one extending this abstract class.
See GenericTraversableTemplate for a standard library example.
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.
I just found the type class concept of Scala and like it actually very much. The problem I have it that all examples I found put the type classes (objects) under one object (Like here: http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html)
Maybe this is wrong but I am a friend of having one file per class. So what I like to do is to put the type classes into several files which would mean that each implementation needs to be put into a seperate object. This now leads to the "problem" that I need to import every single implementation manually. To clarify better here a small example:
package sth
import sth.like.Like
import sth.like.StringLike._
import sth.like.IntLike._
object Application extends App {
def t[T](arg: T)(implicit l: Like[T]) = {
l.print(arg)
}
t(1)
t("Blub")
}
-
package sth.like
trait Like[T] {
def print(arg: T)
}
-
package sth.like
object IntLike {
implicit object LikeIntLike extends Like[Int] {
def print(arg: Int): Unit = println(s"INT $arg")
}
}
-
package sth.like
object StringLike {
implicit object LikeStringLike extends Like[String] {
override def print(arg: String): Unit = println(s"STRING: $arg")
}
}
This works so far. I know, the wildcard import in Application is not necessary but that is not the point. As you can see I need to import both StringLike and IntLike in Application because otherwise these are not available for the Application object.
So is it possible to do this in a generic way or is it completely bad practise to do it this way?
You could put your implicits inside traits, not objects
trait IntLike {
implicit object LikeIntLike extends Like[Int] {
def print(arg: Int): Unit = println(s"INT $arg")
}
}
And then have object with the same name, extending this trait, which would give you what you had earlier, you can import each implicit individually.
object IntLike extends IntLike
You can do the same for all other instances of your typeclass:
trait StringLike {
implicit object LikeStringLike extends Like[String] {
override def print(arg: String): Unit = println(s"STRING: $arg")
}
}
object StringLike extends StringLike
And in another file, you can combine all traits together like this:
object Implicits extends StringLike with IntLike
and import just the Implicits object if you want all your implicits in scope.
package sth
import sth.like.Like
import sth.like.Implicits._
object Application extends App {
def t[T](arg: T)(implicit l: Like[T]) = {
l.print(arg)
}
t(1)
t("Blub")
}
Optionally, you can mixin traits you need
object Application extends App with StringLike with IntLike { ... }
or even do
trait Implicits extends StringLike with IntLike
object Implicits extends Implicits
object Application extends App with Implicits { ... }
thank you very much for your answer. But I also would need to add new type classes manually to the Implicits, wouldn't I?
Couldn't I use then package objects for simplicity reasons like here:
http://naildrivin5.com/scalatour/wiki_pages/PackageObjects/
?
I mean this part:
package opower {
package object controller {
type Secured = org.springframework.security.access.annotation.Secured
type Controller = org.springframework.stereotype.Controller
...
}
}
Since it seems that there is no dynamic way I could just put all type classes into the controller object and could this then import.
Or do I missunderstand the package object?
I'm trying to import a bunch of methods from one class to another without extending it. I've made it work but why one approach works and the other doesn't is beyond me.
Stripped down, here is what I'm trying to do
class A {def x() {println("x")}}
object A
class B {
import A._
def y() {x()}
}
And the compiler tells me "not found: value x"
But it works if I do either this
class C extends A
class B {
import C._
or if I do this
object C extends A
class B {
import C._
Can someone explain why this is the case?
The reason why your code example class C extends A is not working is that you import class members which can only exist in the class they are defined.
Whereas when you write object C extends A you will create a singleton (in Scala called object like the keyword) which represents an instance and allows you to import its members.
So, to make members of other classes visible you always have to extend them, either by an object or by another class/trait. It is not enough to declare a companion object of a class because it does not hold an instance of its companion class.
There also the possibility of using implicits.
You need to have a way to get from a instance of B to the desired instance of
A. In the example below I use a member value, but it should be possible to make
it a function as well.
The trait exposed exposes the this in B to the implicits declared in A.
The trait PathTo can be used to expose a path to the desired instance of A.
class A {
def a1(){ println("a1") };
def a2(){ println("a2") };
def a3(){ println("a3") };
}
object A{
def a1()(implicit a:A){a.a1};
def a2()(implicit a:A){a.a2};
def a3()(implicit a:A){a.a3};
//makes it possible to use a1() instead of a1()(this.a)
implicit def insertPathToA(implicit path:PathTo[A]):A=path.pathTo;
// Makes it possible to write this.a2() instead of this.a.a2();
implicit def convertPathToA(path:PathTo[A]):A=path.pathTo;
};
trait exposed[U]{
implicit def self:U=this.asInstanceOf[U];
}
trait PathTo[U]{
implicit def pathTo:U;
}
class B(val a:A) extends exposed[B] with PathTo[A] {
// imports the magic
import A._
override def pathTo:A=a;
def y() {
a1() ;
this.a2();
};
}