Let's say I have the following hierarchy:
abstract class A(val x: Int, val y: String)
class B(override val x: Int, override val y: String, val z: Int) extends A(x,y)
Now I want to initialize the values from a configuration object but I want the actual values to be the original ones.
If I would do the configuration in B only then I would do something like:
class B(override val x: Int, override val y: String, val z: Int) extends A(x,y)
def this(conf: Conf) {
this(conf.get("x"), conf.get("y"), conf.get("z"))
}
but I want to be able to do the same in A.
If I add:
abstract class A(val x: Int, val y: String)
this(conf: Conf) {
this(conf.get("x"), conf.get("y))
}
I wouldn't be able to define B (I don't have the conf in the B default constructor).
EDIT:
To make this clearer:
The use case I have is a factory which generates the proper B (there are a large number of child classes). It does so by doing something like:
def getElement(elemType: String, conf: Conf): A = {
elemType match {
case "B" => new B(conf)
}
}
Currently, I have a companion object:
object B {
def apply(conf: conf) = B(conf.getx(), conf.gety(), ...)
}
The problem is that when I need to add a new element to the parent A, I need to go and change every one of the children and I have the same code conf.getx(), conf.gety() etc.
Ideally I would like B constructor to be able to do something like:
class B(conf: Conf) extends A(conf)
but I can't do this as this would make conf into a member of B.
You can also use companion objects to define alternative constructors:
case object A {
def apply(conf: Conf): A = new A(conf.get("x"), conf.get("y"))
}
case object B {
def apply(conf: Conf): B = new B(conf.get("x"), conf.get("y"), conf.get("z"))
}
After looking around some more I found this (also points to this) and this. All three basically say the following:
If we use an argument without val or var and it is only referenced in the constructor then it does NOT become a member.
This means the following solution would work:
abstract class A(conf: Conf) {
val x = conf.getX()
val y = conf.getY()
}
class B(conf: Conf) extends A(conf) {
val z = conf.getZ()
}
would provide the required behavior cleanly and simply.
Related
Say I have these two classes:
class A {
def foo(i: String) = println(s"${i}")
def foo(i: String, j: Int) = println(s"${i} ${j}")
}
class B {
def foo(i: Int) = println(s"${i}")
def foo(i: Int, j: String) = println(s"${i} ${j}")
}
I have the variable with the same name as a global variable and as a method in a class:
val inst = A()
abstract class AppB extends App {
def inst = B()
}
I extend the above:
object MyApp extends AppB {
// Should reference A
inst.foo("s11")
inst.foo("s22", 11)
// Should reference B
inst.foo(33)
inst.foo(44, "s33")
}
Assuming I do not want to rename, is there a way to:
Reference the global val inst?
"Shadow" def inst somehow, so that I can only use the global val inst one?
From https://dotty.epfl.ch/docs/reference/dropped-features/package-objects.html:
If a source file src.scala contains such top-level definitions, they will be put in a synthetic object named src$package
Maybe using the "auto-generated" object name to access val inst can work in this case?
Say I have the following trait:
trait T {
val x: Int
}
def foo(i: Int): T
I would like to bet able to write and read this trait using upickle without knowing what the specific class is. e.g.:
upickle.default.write(foo(3))
Such that I could elsewhere define foo to be something like:
case class A(x: Int)
def foo(i: Int): T = A(i)
I am thinking I need to define an implicit Writer as a member of T but I don't know what the appropriate syntax would be?
trait T {
val x: Int
}
object T {
implicit val rw: ReaderWriter[T] = ...
}
The problem is what to put into the ... part: if you have a T value, you can just store its x:
... = readwriter[Int].bimap[T](
t => t.x,
i => new T { val x = i }
)
The problem with this solution is that reading a written A(3) won't return an A. And this isn't really solvable without making T sealed or otherwise handling a specific set of subclasses only.
You could include a class name as well when writing, but that won't help if the class has any fields other than x to store:
class B(override val x: Int, y: String) extends T
If T is sealed, you just write
... = macroRW
Is there exist in Scala some syntaxic sugar to "immutable class setters"?
Here is an example:
class Bob (val x:Int, val y:String)
In order to change x or y, I can implement it this way :
object Bob {
def updX (b:Bob) (x:Int) = new Bob (x, b.y)
def updY (b:Bob) (y:String) = new Bob (b.x, y)
}
class Bob (val x:Int, val y:String) {
def updX (x:Int) = Bob.updX(this)(x)
def updY (y:String) = Bob.updY(this)(y)
}
This solution is terrible because some of my classes have 8 parameters.
Is there a better way to achieve this ?
If you make your class a case class, you will get a copy method for free. Case class parameters are automatically immutable, so no need to put val in front of them.
case class Bob(x: Int, y: String)
val bob1 = Bob(1, "Bob1")
val bob2 = bob1.copy(y = "Bob2")
As #insan-e mentions in his answer, the pattern that is used by case classes, is a copy method with parameters matching the constructor and default values matching the vals. You can easily implement that yourself:
class Bob(val x: Int, val y: String) {
def copy(x: Int = x, y: String = y) = new Bob(x, y)
}
I have two classes with some data members like this:
class MyInfo {
private val myId: String
private val time: DateTime
private val solution: Long
private val banner: Int
}
Class HisInfo {
private val id: String
private val solution: Long
private val banner: Int
}
As you can see these two classes share two members, and in my real project they share more. I need to save them into hbase and designed a class like this:
sealed trait Element[T] {
def serialize(value: T)(implicit val helper: Helper[T]): Array[Byte]
def deserialize(bytes: Array[Byte])(implicit val helper: Helper[T]): T
}
case object LongElement extends Element[Long] {...}
case object IntElement extends Element[Int] {...}
class Info {
protected val data: Map[Element[_], Any] = new mutable.Map[Element[_], Any]()
}
class MyInfo extends Info {
val elements = List(LongElement, IntLement)
def saveToHBase = {
elements.foreach { e =>
val v = e.serialize(data(e))
// do sth with v
}
}
In fact I have defined implementations of Helper[Int] and Helper[Long], but the compiler complains that it can not find implicit value for parameter Helper[_1]. Can someone help me to design these classes?
In fact I have defined implementations of Helper[Int] and Helper[Long], but the compiler complains that it can not find implicit value for parameter Helper[_1].
Well, consider what would happen if elements included an Element[String] (or some other type for which you have no implicit Helper). Given the type of elements, the compiler can't know it doesn't.
I think that if you need a Helper for all methods of Element anyway, you should just make it a part of the type:
sealed trait Element[T] {
val helper: Helper[T] // or def if you want to put it in the companion object
def serialize(value: T): Array[Byte]
def deserialize(bytes: Array[Byte]): T
}
or
sealed abstract class Element[T](implicit val helper: Helper[T]) {
def serialize(value: T): Array[Byte]
def deserialize(bytes: Array[Byte]): T
}
At least in most situations.
It definitely looks like work for shapeless
Cool thing about shapeless, it can convert your class to list like structure named HList.
Scala ordinary collections like List should drop information about elements to bring them to common type.
HList could save type of each elements, while providing List-like functionality
Lets define your type with their common fields in separate type
import org.joda.time.DateTime
class Common(
val solution: Long,
val banner: Int
)
class MyInfo(
myId: String,
time: DateTime,
solution: Long,
banner: Int
) extends Common(solution, banner)
class HistInfo(
id: String,
solution: Long,
banner: Int
) extends Common(solution, banner)
Now lets define something looks like serialization:
trait Helper[T] extends (T => Array[Byte])
implicit object longHelper extends Helper[Long] {
def apply(x: Long) = 0 to 7 map (i => (x >> (i * 8)).toByte) toArray
}
implicit object intHelper extends Helper[Int] {
def apply(x: Int) = 0 to 3 map (i => (x >> (i * 8)).toByte) toArray
}
Now something interesting. We'll create special object that could convert your Common type to special HList which contains every value preserving it's type information and statically-saved string with field name:
import shapeless._
val lgen = LabelledGeneric[Common]
Next we define special function-like thing to map over such HList. It would find known implicit Helper and tie its result with corresponding field name:
import shapeless.labelled.FieldType
object serialized extends Poly1 {
implicit def serialize[K <: Symbol, T]
(implicit helper: Helper[T], key: Witness.Aux[K])
= at[FieldType[K, T]](field => key.value.name -> helper(field))
}
Now we define some user of this function:
def extractMap(x: Common): Map[String, Seq[Byte]] =
lgen.to(histInfo).map(serialized).toList.toMap.mapValues(_.toSeq)
You could verify you function is working:
val histInfo = new HistInfo("123", 12, 3)
println(extractMap(histInfo))
will print
Map(solution -> WrappedArray(12, 0, 0, 0, 0, 0, 0, 0), banner ->
WrappedArray(3, 0, 0, 0))
situation:
trait Operation {
def something: Double
}
trait OperationPlus { this: A =>
override def something: Double = x + y
}
trait OperationMinus { this: A =>
override def something: Double = x - y
}
case class A(x: Double, y: Double) { this: Operation =>
}
val a = new A(1.0, 2.0) with OperationPlus
println(a.something)
error:
class A cannot be instantiated because it does not conform to its self-type A with Operation
Also, i can't instantiate A.
I tried multiple different approaches, but none provided what i'm looking for. I don't want to use case class inheritance, or give up case classes, and ideally trait / self types / something else should do the trick. Any ideas?
Update
preferred solution
trait Operation { this: A =>
def something: Double
}
trait OperationPlus extends Operation { this: A =>
override def something: Double = x + y
}
trait OperationMinus extends Operation { this: A =>
override def something: Double = x - y
}
abstract case class A(val x: Double, val y: Double) extends Operation
val a = new A(1.0, 2.0) with OperationPlus
println(a.something)
val b = new A(1.0, 2.0) with OperationMinus
println(b.something)
possible solution 1:
trait Operation {
def x:Double
def y:Double
def something: Double
}
trait OperationPlus extends Operation {
override def something: Double = x + y
}
trait OperationMinus extends Operation {
override def something: Double = x - y
}
abstract case class A(val x: Double, val y: Double) extends Operation
By using conventional classes, simple trait inheritance and a self-type in the actual value is possible to define it and supply behaviour dynamically.
Unfortunately, I have to redefine the fields in the trait. I guess is a fair compromise. Would be interested to know if somebody knows of another approach.
Thanks
Not sure about your use case, but you need to define class A like this if you want to make it work:
abstract case class A(x: Double, y: Double) extends Operation
But I don't think is very idiomatic approach to use case classes. They are mostly used as data containers and normally do not contain any behavior. (maybe you can tell some more information about things you want to achieve with this)
First, you should have:
trait OperationPlus extends Operation
trait OperationMinus extends Operation
Second, you cannot define A as case class, since that automatically defines the apply method on the companion object, where new A is called (with arguments). This call fails due to the wrong self type (the error you see).
Remove the case. If you need pattern matching, define an extractor yourself (implement unapply):
class A(val x: Double, val y: Double) { this: Operation => }
object A {
def unapply(v: A) = Some((v.x, v.y))
}