I have a Scala case class that has certain val's as parameters. I have a couple of methods that makes use of this case class. Assuming that my case class is defined as follows:
case class CaseA(a: Int, b: List[CaseB])
case class CaseB(a: Int, b: text, c: Boolean)
Both the CaseA and CaseB represent the domain model of my case classes. But as I use them in my application, I want to add some behavior to CaseA based on CaseB's Boolean field. For example., let us suppose that in CaseA, the val b has 4 CaseB objects. I want to add a behavior that would run through b and tell me if any one of the element in CaseB has the val c set to true.
What I did come up is with the following implementation of CaseA:
case class CaseA(a: Int, b: List[CaseB], c: Boolean)
But the problem with this approach is that when I load my CaseA object from the database, I do not need to have the val c as the val c is computed and not part of CaseA object. I went a step further and modified my CaseA to look like as below:
case class CaseA(a: Int, b: List[CaseB], c: Option[Boolean])
I can see that it is already getting ugly. How can I add behavior to my case classes so that the val c need not be part of the CaseA object and it is rather computed everytime at runtime?
I would use a lazy value in case you don't need the c for all elements
case class CaseB(a:Int,b:String,c:Boolean)
case class CaseA(a:Int,b:List[CaseB]){
lazy val c = b exists (_.c)
}
val ca = CaseA(42, List(
CaseB(1,"hello",false),
CaseB(2,",",false),
CaseB(3,"world",true))
)
println(ca.c)
To answer some of your comment questions, if you want to use a mixin-trait, you can do something like the following:
case class CaseB(a:Int,b:String,c:Boolean)
case class CaseA(a:Int,b:List[CaseB])
trait CanCheck extends CaseA{
lazy val c = b exists (_.c)
}
val ca = new CaseA(42, List(
CaseB(1,"hello",false),
CaseB(2,",",false),
CaseB(3,"world",true))
) with CanCheck
println(ca.c)
Notice that you need to use the new keyword to make this work
In a two ways:
1) define c as a val, or even better, as a def:
case class CaseA(a: Int, b: List[CaseB]){
val c = b.exists(_.c)
}
2) use implicit wrappers:
case class CaseA(a: Int, b: List[CaseB])
implicit class CaseAC(underlying: CaseA) {
val c = underlying.b.exists(_.c)
}
val x = CaseA(3, Nil)
// x: CaseA = CaseA(3,List())
x.c
// Boolean = false
If you don't need to store c, you might use implicit value classes, which are even thinner wrapper
I don't understand what you are doing with a database. Ignoring that bit, I'd do the following:
case class CaseA(a: Int, b: List[CaseB]){
val c = b.exists(_.c)
}
Related
I'm brand new to shapeless and I would like to transform a Mapper[mix.type, HNil]#Out to a case class
How can I do this? (Let me know if you need more infos...)
That would only work if you define a case class that has the exact same shape that this Mapper#Out. If that's the case, you can create an instance of your case class using shapeless.Generic:
val mout = ... // HList coming from your Mapper
case class A(i: Int, s: String)
shapeless.Generic[A].from(mout): A
That's assuming Generic#Repr and Mapper[mix.type, HNil]#Out are the same type, which you can check using the following:
val mapper = the[Mapper[mix]]
val gen = the[Generic[A]]
implicitly[mapper#Out =:= gen#Repr] // This only complies if scalac can
// prove equality between these types
I was thinking about something, I wrote this trait with two classes extending it and another class that may contain them:
sealed trait MainObj
case object subObj1 extends mainObj
case Object subObj2 extends mainObj
case class AnotherClass(val mo: Option[MainObj], val amount: Int)
Now let's say I wanted to write a function that added the amounts in twoAnotherClass objects but only if the optional MainObj inside each of them were the same type. Like only add if both are subObj1.
So I could do something like this:
def add(one: AnotherClass, two: AnotherClass): AnotherClass = one. mo, two.mo match {
case (Some(x), Some(y)) if i and x are the same class => return a `AnotherClass` with the two integers added from `one` and `two`
etc..
I wondered whether there was another way of doing this? I don't know whether this is a "functional" way of doing it or whether there is a less verbose way?
I ask because essentially I could write some more methods like subtract, multiply, etc ... and I have some very common code.
I could extract that common code and write a function that simply takes two of the subObjs and an operation and then apply that operation on the two objects, but that only abstracts out the common operation, could the main pattern matching part be written differently that may be considered more concise(for lack of a better word)?
Just add the 'add' method to the case class :
sealed trait MainObj
case object SubObj1 extends MainObj
case object SubObj2 extends MainObj
case class AnotherClass(val mo: Option[MainObj], val amount: Int){
def add(that:AnotherClass):AnotherClass = {
if (that.mo == this.mo)
this.copy(amount = this.amount + 1)
else
this
}
}
val x = AnotherClass(Some(SubObj1), 1)
val y = AnotherClass(Some(SubObj1), 1)
val z = AnotherClass(Some(SubObj2), 1)
scala> x add y
res5: AnotherClass = AnotherClass(Some(SubObj1),2)
scala> x add z
res6: AnotherClass = AnotherClass(Some(SubObj1),1)
scala> val l = List(x,y,z)
l.reduce ( _ add _)
res7: AnotherClass = AnotherClass(Some(SubObj1),2)
val mo = two.mo
one match {
case AnoterClass(`mo`, am) =>
one.copy(amount = am + two.amount)
case _ => ???
}
{
class MyClass(name: String) {}
val x = new MyClass("x")
println(x.name) // Error name is not a member of MyClass
}
but
{
abstract class Base
case class MyClass(name: String) extends Base {}
var x = new MyClass("x")
println(x.name) // name is a member of MyClass
}
So, what's the deal with case classes? Why are all of the constructor parameters turned into variables.
name is member in both examples, but private in your first example while public in your second. Case classes make their constructor parameters public val by default.
Pattern matching is the most important but not the only application for case classes. Another important point is that they implement the equals and hashCode methods in terms of the constructor arguments (aka product elements). Therefore, case classes are very useful for defining data structures that serve as elements in sets or keys in maps. That in turn only makes sense if these elements are visible.
Compare:
class Foo(val i: Int)
val set1 = Set(new Foo(33))
set1.contains(new Foo(33)) // false!!
And:
case class Bar(val i: Int)
val set2 = Set(Bar(33)
set2.contains(Bar(33)) // true!
Two case class instances with equal parameters are equal themselves. You can imagine them representing some "constants". This implies that you should not have mutable state in them.
You can, however, use a second parameter list to exclude arguments from the equality:
case class Baz(i: Int)(val n: Long)
Baz(33)(5L) == Baz(33)(6L) // true!
Another useful feature which implies that the constructor arguments become values, is making copies. This is the way immutable data is changes—you create a new instance with a particular value changed, leaving the original value in place.
case class Person(name: String, age: Int)
val p1 = Person("Fuzzi", 33)
val p2 = p1.copy(age = 34)
The copy method uses default values for all unspecified argument, taking those values from the constructor args.
Just to be clear, the constructor arguments aren't used to create variables, they're used to create values.
If you specify val in your first example, the non-case class:
class MyClass(val name: String) {}
then you also get the argument translated into a public value, the same as is done for the case class.
In the example on the Scala-Lang site it says:
It makes only sense to define case classes if pattern matching is used
to decompose data structures. The following object defines a pretty
printer function for our lambda calculus representation:
followed by the example code:
object TermTest extends Application { def printTerm(term: Term) {
term match {
case Var(n) =>
print(n)
case Fun(x, b) =>
print("^" + x + ".")
printTerm(b)
case App(f, v) =>
Console.print("(")
printTerm(f)
print(" ")
printTerm(v)
print(")")
} } def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false } val id = Fun("x", Var("x")) val t = Fun("x", Fun("y", App(Var("x"), Var("y")))) printTerm(t) println println(isIdentityFun(id)) println(isIdentityFun(t)) }
To add something to my comment due to lack of available space: consider the following example case class:
case class My(x: Int)
If you save it to file and pass it to scalac -print, you get following expanded code (I removed unimportant stuff):
case class My extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def x(): Int = My.this.x;
Notice <caseaccessor>s here.
And then companion object:
<synthetic> object My extends runtime.AbstractFunction1 with Serializable {
case <synthetic> def apply(x: Int): My = new My(x);
case <synthetic> def unapply(x$0: My): Option = if (x$0.==(null))
scala.this.None
else
new Some(scala.Int.box(x$0.x()));
case <synthetic> <bridge> def apply(v1: Object): Object = My.this.apply(scala.Int.unbox(v1));
//...
Notice apply and unapply here. If you look at complete output yourself, you'll learn more about how scala generates your code.
I have two case classes that inherit from an abstract base class. I want to define some methods on the abstract base class that use the copy methods on the inheriting case classes (and so return an instance of the child class.) Is there a way to do this using self types?
Example code:
abstract class BaseClass(a: String, b: Int) {
this: case class => //not legal, but I'm looking for something similar
def doubleB(newB: Int) = this.copy(b = b * 2) //doesn't work because BaseClass has no copy
}
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass(a, b) {
def doesStuffWithC(newC: Boolean) = {
...
}
}
case class HasD(a: String, b: Int, D: Double) extends BaseClass(a, b) {
def doesStuffWithD(newD: Double) = {
...
}
}
I've figured out how to get the result I want thanks to this question:
How to use Scala's this typing, abstract types, etc. to implement a Self type?
but it involves adding a makeCopy method to BaseClass and overriding it with a call to copy in each of the child case classes, and the syntax (especially for the Self type) is fairly confusing. Is there a way to do this with Scala's built in self typing?
You can't do what you want because copy needs to know about all the possible parameters. So even if case classes inherited from Copyable, it wouldn't be the copy you needed. Also, if you're going to keep the types straight, you'll be thwarted by Scala's lack of a "MyType". So you can't just extend a base class. However, you could add an abstract method and type annotation:
abstract class BaseClass[C <: BaseClass[_]](a: String, b: Int) {
def setB(b0: Int): C
def doubleB(b0: Int) = setB(b0*2)
}
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass[HasC](a,b) {
def setB(b0: Int) = this.copy(b = b0)
def doesStuffWithC(c0: Boolean) = doubleB(if (c0) b else -b).copy(c = c0)
}
And then you can:
scala> HasC("fish",1,false).doesStuffWithC(true)
res47: HasC = HasC(fish,2,true)
This extra work will be worth it if you have a lot of shared functionality that depends on the ability to copy just b (either many methods, or a small number of complicated methods)--that is, this solves the DRY issue. If instead you want to abstract over HasC and other derived classes, you can either use BaseClass[_] or add yet another level that defines setB(b0: Int): BaseBase or simply forget the type parameterization and use BaseClass as the return type (but recognize that HasC cannot use BaseClass methods and still retain its type identity).
I think you're out of luck. The copy methods on HasC and HasD have different signatures. It's a bit hidden because of the default arguments, but basically the definition in BaseClass wouldn't know which copy method to call.
You could define a makeCopy in the abstract class that takes a copier function that takes Unit and returns a BaseClass, then, in your methods that use it (like doubleB) override them in the case class bodies and make use of makeCopy by passing it an anonymous function that does the work of creating a new copy with the props changed, like so:
package delegatedcopy
abstract class BaseClass(a: String, b:Int){
def aField = a
def bField = b
def doubleB:BaseClass
def makeCopy(copier: () => BaseClass):BaseClass = copier()
}
case class HasC(override val aField: String, override val bField: Int, cField: Boolean) extends BaseClass(aField, bField){
override def doubleB:BaseClass = makeCopy( ()=> HasC(aField, bField * 2, cField) )
}
case class HasD(override val aField: String, override val bField: Int, dField:Double) extends BaseClass(aField, bField){
override def doubleB:BaseClass = makeCopy( ()=> HasD(aField, bField * 2, dField) )
}
A test app that demonstrates it:
import delegatedcopy._
object TestApp extends Application{
val hasC = HasC( "A C object", 5, true)
val hasD = HasD( "A D object", 2, 3.55)
val hasCDoubleB = hasC.doubleB
val hasDDoubleB = hasD.doubleB
println(hasC) // prints HasC(A C object,5,true)
println(hasCDoubleB) //prints HasC(A C object,10,true)
println( hasD ) // prints HasD(A D object,2,3.55)
println( hasDDoubleB ) // prints HasD(A D object,4,3.55)
}
In this way, you are able to keep the makeCopy method the same for all children classes as in the base class, and can probably implement or mix in quite a bit of functionality in the base and case classes while keeping common code in a safe place and being able to pass clients a BaseClass and pattern match on the specific case classes.
I have a hierarchy like the following:
case class A(val a: Long, val b: String)
case class B(val c: String) extends A(a=3, b="a string")
and I'm trying to serialize it using lift-json ala the following:
val obj = B(c="another string")
val cameraJson = net.liftweb.json.Serialization.write(obj)
but what I'm seeing is that it only serializes the properties in class B and not those in A.
I've also tried:
compact(render(decompose(obj)))
with the same result
What gives? Is there something obvious in Scala that I'm missing?
case class inheritance is a deprecated feature of Scala. This should work for instance:
trait A { val a: Long; val b: String }
case class B(a: Long = 3, b: String = "a string", c: String) extends A
val obj = B(c="another string")
var ser = Serialization.write(obj)
Serialization.read[B](ser)
Classic lift JSON serialisation for case classes is based on constructor argument list (see decompose implementation), not class attributes. So you have to either override all fields declared in the parent trait (as in the #Joni answer) or use composition instead of inheritance.
For example:
case class A(a: Long, b: String)
case class B(c: String, a: A = A(a=3, b="a string"))
B(c="another string")
BTW val keyword in case class constructor is unnecessary. The accessors for every constructor arg is one of the things you are getting for free by declaring class as a case.
IMO serializing only c seems like the right thing to do. The info appearing in serialization will be that the type is B and the value of C. Values of a and b are implied by the info type is B. They are 3 and "a string", and can be nothing else, so they do not need to be written.
From maybe too cursory a look at the source code, the default behavior of is to serialize fields that correspond to parameters of the primary constructor