I know I can add a companion object to a class for static def/val but my problem is I want it to comfirm to a trait. I want access to this static val or def as an abstract type (using the word static here in terms of the behaviour expected from java static)
Consider this code
trait A {
def aBehavior(): Unit
}
trait B {
def bBehavior(): Unit
def description: String
}
case class M extends A with B {
override def aBehavior() = print("A behavior of M")
override def bBehavior() = print("B behavior of M")
override def description = "I'm M"
}
I want to be able to call M.description as a static method without having an instance of M. My use case is I have a spark dataset of M objects and I want to see the description property of M without getting a record from the dataset because that will result in a spark action / job
Is there a scala pattern I can use for this. Thanks
Just create a companion object for M which defines the static value and then reference that in the case class
object M {
val description = "I'm M"
}
case class M extends A with B {
override def description = M.description
}
or assuming commonality between subtypes
trait Description {
val title: String
val description = s"I'm ${title}"
}
object M extends Description {
val title = "M"
}
object N extends Description {
val title = "N"
}
case class M() extends A with B {
override def description = M.description
}
case class N() extends A with B {
override def description = N.description
}
You can refactor description of B into another trait like:
trait BInfo {
def description: String
}
trait B extends BInfo {
def bBehavior(): Unit
def bInfo: BInfo
final override def description = bInfo.description
}
case class M() extends A with B {
override def aBehavior() = print("A behavior of M")
override def bBehavior() = print("B behavior of M")
override def bInfo = M
}
object M extends BInfo {
override def description = "I'm M"
}
val m = M()
M.description // I'm M
m.description // I'm M
Related
I want to bind a check method to the Test in such a way that the implementation does not contain an argument (look at the last line). It is necessary to use type classes here, but I'm new in Scala, so I have problems.
Object Checker is my attempts to solve the problem. Perhaps it is enough to make changes to it...
trait Test[+T] extends Iterable[T]
class TestIterable[+T](iterable: Iterable[T]) extends Test[T] {
override def iterator: Iterator[T] = iterable.iterator
}
object Test {
def apply[T](iterable: Iterable[T]): Test[T] = new TestIterable[T](iterable)
}
trait Check[M] {
def check(m: M): M
}
object Checker {
def apply[M](implicit instance: Check[M]): Check[M] = instance
implicit def checkSyntax[M: Check](m: M): CheckOps[M] = new CheckOps[M](m)
private implicit def test[T](m: Test[T]) : Check[Test[T]] = {
new Check[Test[T]] {
override def check(m: Test[T]) = m
}
}
final class CheckOps[M: Check](m: M) {
def x2: M = Checker[M].check(m)
}
}
import Checker._
val test123 = Test(Seq(1, 2, 3))
Test(test123).check
This is the simplified code of a class(object) I'm working on:
object T {
val default = A
val options = List(A,B,C)
sealed trait T
object A extends T {
override def toString = "A"
}
object B extends T {
override def toString = "B"
}
object C extends T {
override def toString = "C"
}
}
This hierarchy maps directly to GUI element which requires a options = List(A,B,C) to build.
Problem with current approach:
Every time I add an extra object I have to change the option manually. This isn't too much work however the code isn't too elegant this way.
My question is:
Can I generate a list of inner objects during compile time? I wouldn't like to do this during runtime, it would be an overkill.
To add to #Samar's comment, to make it clear. The following is what you need:
import scala.reflect.runtime.universe._
case object K {
val default = A
val options = getObjects[this.type]
sealed trait T
object A extends T {
override def toString = "A"
}
object B extends T {
override def toString = "B"
}
object C extends T {
override def toString = "C"
}
def getObjects[T: TypeTag] = typeOf[T].members.collect {
case m: ModuleSymbol if m.isModule => m
}.toList
}
I have abstract class A
abstract class A{
def this(obj:Object){
this()
obj match{
case s:String => stringMethod(s)
case n:Int => intMethod(n)
}
def stringMethod(s:String)
def intMethod(n:Int)
}
and I have a class that extends this class
class B(obj:Object) extends A(obj){
var s:String = null
def stringMethod(s:String){
this.s = s
}
def intMethod(n:Int){
this.s = n.toString
}
}
The point of this class is to instantiate an object and its variables depending on the class type of the object used to instantiate it but the problem is that when the abstract constructor is called, the default constructor of object which is extending the abstract object is somehow being called after. This alters the value of var s back to null.
This a really simple implementation of my classes and I have more variables in class B and more logic inside intMethod and stringMethod.
I realize this might be a completely wrong way of doing this, so if there is a better way please let me know.
The body of a super class is always executed before the body of a sub class. In your case A calls stringMethod or intMethod first, then finally B's body is executed, assign null to s. If you remove that assignment, it should work:
abstract class A{
def this(obj:Object){
this()
obj match{
case s:String => stringMethod(s)
case n:Int => intMethod(n)
}
}
def stringMethod(s:String)
def intMethod(n:Int)
}
class B(obj:Object) extends A(obj){
var s:String = _ // !
def stringMethod(s:String){
this.s = s
}
def intMethod(n:Int){
this.s = n.toString
}
}
val b = new B("foo")
b.s
Neverless, the style is problematic. Here are two alternatives:
trait A {
def obj: Any
def s: String
}
class B(val obj: Any) extends A {
val s = obj match {
case i: Int => i.toString
case x: String => x
case x => throw new IllegalArgumentException(x.toString)
}
}
Or better statically checked:
object B {
def apply(i: Int ): B = new B(i, i.toString)
def apply(s: String): B = new B(s, s)
}
class B private(val obj: Any, val s: String) extends A
B(1.0) // not allowed
Suppose I have some abstract value field defined in a trait:
trait Base {
val toBeOverride: String
}
case class Impl(other:Int) extends Base {
override val toBeOverride = "some value"
}
How can I write a function that I can easily get a cloned instance only overriding the toBeOverride value, like this:
// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
?
Edit
#som-snytt, I don't think this is a duplicate, just like a Trait is not the same as an Abstract Class. And the answers of that question do not satisfy me, see below.
#Blaisorblade, yes, it is a problem. For instances of each sub case class, the toBeOverride field are the same, so it should not appear in the constructor.
For now all the suggestions are to define an customized copy method in each(!) sub case class and that in my opinion is ugly and shows the incapability of the language.
The simplest solution is to just add the method you want to Base:
trait Base {
val toBeOverride: String
def copyBase(newToBeOverridden: String): Base
}
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}
This also allows to directly create an instance of Impl while specifying the value of toBeOverride (which wasn't possible). The only disadvantage is that now pattern matches using Impl have to change syntax - please update your question and add a comment if that's a problem.
BTW, if you just want to add a prefix (as in your example), that's no problem:
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}
Here are two mechanisms.
Apparently, in the near future you'll be able to write a macro that can emit the anonymous subclass, but until then, I think this typeclass is not arduous.
Just kicking the tires on Dynamic here.
import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
trait Base {
def m: String
}
case class Impl(p: Int) extends Base {
override val m = "some value"
}
trait Basic extends Dynamic {
protected def m: String
def selectDynamic(f: String): Any =
if ("m" == f) m else reflecting(this, f)
protected def reflecting(b: Basic, f: String) = {
val im = cm.reflect(b)
val member = im.symbol.typeSignature member newTermName(f)
require(member != NoSymbol, s"No such member $f")
(im reflectMethod member.asMethod)()
}
}
case class Implic(p: Int) extends Basic {
override protected val m = "some value"
}
object Test extends App {
implicit class Copy[A <: Base](val b: A) {
def overriding(overm: String): A = (b match {
case impl: Impl => new Impl(impl.p) { override val m = overm }
case b: Base => new Base { override val m = overm }
}).asInstanceOf[A]
}
implicit class Proxy[A <: Basic : ClassTag](val b: A) {
def proximately(overm: String): Basic = new Basic {
override val m = overm
override def selectDynamic(f: String): Any =
if ("m" == f) overm else reflecting(b, f)
override def toString = b.toString
}
}
// asked for this
//def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)
/* want something like this
def overriding[T <: Base](v: Base) = new Impl(v.p) {
override val m = "some value"
} */
val a = Impl(5)
val b = a overriding "bee good"
Console println s"$a with ${a.m} ~> $b with ${b.m}"
// or
val c = Implic(7)
val d = c proximately "dynomite"
Console println s"$c with ${c.m} ~> $d with ${d.m}"
}
Since traits don't get copy methods automatically, you can try using a Base case class instead:
case class Base(toBeOverride: String)
case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
The problem that you're going to run into though, is that copy returns an instance of Base and I don't think that you can convert it back to your original Impl class. For instance, this won't compile:
def overrideBaseValue[T <: Base](v: T): T =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
IntHistogram counts words and displays them count-descending:
object IntHistogram {
def main(args: Array[String]) {
val wordsToCount = "foo foo bar foo bar wtf foo bar".split(" ")
val histogram = new IntHistogram
for (word <- wordsToCount) histogram(word) += 1
println(histogram)
/*
(foo,4)
(bar,3)
(wtf,1)
*/
}
}
class IntHistogram extends collection.mutable.HashMap[String,Int] {
override def default(key:String) = 0
def descendingPairs = toList.sortBy(_._2).reverse
override def toString() = descendingPairs.mkString("\n")
}
I needed a DoubleHistogram, and I resorted to copy-and-paste because I couldn't figure out how to define a generic "Histogram[NumberSuperClassOfIntAndDouble]" trait:
class DoubleHistogram extends collection.mutable.HashMap[String,Double] {
override def default(key:String) = 0
def descendingPairs = toList.sortBy(_._2).reverse
override def toString() = descendingPairs.mkString("\n")
}
Can a smart/knowledgeable person show me how to define such a supertrait, so I can avoid the ugly copy-and-paste boilerplate?
Thanks in advance,
PT
P.S. I really want Histogram to be a trait, so I can mix the Histogram behavior into any numerically-valued Map. In reality there are lots of behaviors I need, besides a descending toString method; I simplified it for this question. Here Num is the fictional numeric superclass of Int and Double:
trait Histogram[Num] extends collection.Map[String,Num] {
override def default(key:String) = 0
def descendingPairs = toList.sortBy(_._2).reverse
override def toString() = descendingPairs.mkString("\n")
}
I tried using Numeric, Number, import Ordering.Implicits._, all kinds of stuff... to no avail.
This seems to work just fine with Numeric. Not that you can't make Histogram a trait because of the context bound on N, but IntHistogram and DoubleHistogram can be traits.
object IntHistogram {
def main(args: Array[String]) {
val wordsToCount = "foo foo bar foo bar wtf foo bar".split(" ")
val histogram = new IntHistogram {}
for (word <- wordsToCount) histogram(word) += 1
println(histogram)
/*
(foo,4)
(bar,3)
(wtf,1)
*/
}
}
abstract class Histogram[N:Numeric] extends collection.mutable.HashMap[String,N] {
override def default(key:String) = implicitly[Numeric[N]].zero
def descendingPairs = toList.sortBy(_._2).reverse
override def toString = descendingPairs.mkString("\n")
}
trait IntHistogram extends Histogram[Int]
trait DoubleHistogram extends Histogram[Double]
If you really, really need Histogram to be a trait, you can do it this way:
trait Histogram[N] extends collection.mutable.HashMap[String,N] {
implicit val n:Numeric[N]
override def default(key:String) = n.zero
def descendingPairs = toList.sortBy(_._2).reverse
override def toString = descendingPairs.mkString("\n")
}
But then you have to instantiate it like this in main:
val histogram = new Histogram[Int] { val n = implicitly[Numeric[Int]] }