{
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.
Related
Assuming that I have a Generic superclass:
class GenericExample[T](
a: String,
b: T
) {
def fn(i: T): T = b
}
and a concrete subclass:
case class Example(
a: String,
b: Int
) extends GenericExample[Int](a, b)
I want to get the type parameter of function "fn" by scala reflection, so I select and filter through its members:
import ScalaReflection.universe._
val baseType = typeTag[Example]
val member = baseType
.tpe
.member(methodName: TermName)
.asTerm
.alternatives
.map(_.asMethod)
.head
val paramss = member.paramss
val actualTypess: List[List[Type]] = paramss.map {
params =>
params.map {
param =>
param.typeSignature
}
}
I was expecting scala to give me the correct result, which is List(List(Int)), instead I only got the generic List(List(T))
Crunching through the document I found that typeSignature is the culprit:
* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
* instantiation of a generic type.
And it suggests me to use the alternative:
def typeSignatureIn(site: Type): Type
However, since class Example is no longer generic, there is no way I can get site from typeTag[Example], can anyone suggest me how to get typeOf[Int] given only typeTag[Example]? Or there is no way to do it and I have to revert to Java reflection?
Thanks a lot for your help.
UPDATE: After some quick test I found that even MethodSymbol.returnType doesn't work as intended, the following code:
member.returnType
also yield T, annd it can't be corrected by asSeenFrom, as the following code doesn't change the result:
member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass)
There are two approaches which I can suggest:
1) Reveal generic type from base class:
import scala.reflect.runtime.universe._
class GenericExample[T: TypeTag](a: String, b: T) {
def fn(i: T) = "" + b + i
}
case class Example(a: String, b: Int) extends GenericExample[Int](a, b) {}
val classType = typeOf[Example].typeSymbol.asClass
val baseClassType = typeOf[GenericExample[_]].typeSymbol.asClass
val baseType = internal.thisType(classType).baseType(baseClassType)
baseType.typeArgs.head // returns reflect.runtime.universe.Type = scala.Int
2) Add implicit method which returns type:
import scala.reflect.runtime.universe._
class GenericExample[T](a: String, b: T) {
def fn(i: T) = "" + b + i
}
case class Example(a: String, b: Int) extends GenericExample[Int](a, b)
implicit class TypeDetector[T: TypeTag](related: GenericExample[T]) {
def getType(): Type = {
typeOf[T]
}
}
new Example("", 1).getType() // returns reflect.runtime.universe.Type = Int
I'm posting my solution: I think there is no alternative due to Scala's design:
The core difference between methods in Scala reflection & Java reflection is currying: Scala method comprises of many pairs of brackets, calling a method with arguments first merely constructs an anonymous class that can take more pairs of brackets, or if there is no more bracket left, constructs a NullaryMethod class (a.k.a. call-by-name) that can be resolved to yield the result of the method. So types of scala method is only resolved at this level, when method is already broken into Method & NullaryMethod Signatures.
As a result it becomes clear that the result type can only be get using recursion:
private def methodSignatureToParameter_ReturnTypes(tpe: Type): (List[List[Type]], Type) = {
tpe match {
case n: NullaryMethodType =>
Nil -> n.resultType
case m: MethodType =>
val paramTypes: List[Type] = m.params.map(_.typeSignatureIn(tpe))
val downstream = methodSignatureToParameter_ReturnTypes(m.resultType)
downstream.copy(_1 = List(paramTypes) ++ methodSignatureToParameter_ReturnTypes(m.resultType)._1)
case _ =>
Nil -> tpe
}
}
def getParameter_ReturnTypes(symbol: MethodSymbol, impl: Type) = {
val signature = symbol.typeSignatureIn(impl)
val result = methodSignatureToParameter_ReturnTypes(signature)
result
}
Where impl is the class that owns the method, and symbol is what you obtained from Type.member(s) by scala reflection
Given the following classes:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
How do I define in a typesafe manner some function:
def process(req: ???): ???
Such that the following should hold true:
val r1: AddResponse = process(AddRequest(2, 3))
val r2: ToUppercaseResponse = process(ToUppercaseRequest("aaa"))
Also, the following should not compile:
val r3 = process("somestring")
This is both entirely possible and a totally reasonable thing to do in Scala. This kind of thing is all over Shapeless, for example, and something similar (but less principled) is the basis of the magnet pattern that shows up in Spray, etc.
Update: note that the following solution assumes that "given the following classes" means you don't want to touch the case classes themselves. If you don't care, see the second part of the answer below.
You'd want a type class that maps input types to output types:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
trait Processable[In] {
type Out
def apply(in: In): Out
}
And then some type class instances:
object Processable {
type Aux[I, O] = Processable[I] { type Out = O }
implicit val toUppercase: Aux[ToUppercaseRequest, ToUppercaseResponse] =
new Processable[ToUppercaseRequest] {
type Out = ToUppercaseResponse
def apply(in: ToUppercaseRequest): ToUppercaseResponse =
ToUppercaseResponse(in.str.toUpperCase)
}
implicit val add: Aux[AddRequest, AddResponse] =
new Processable[AddRequest] {
type Out = AddResponse
def apply(in: AddRequest): AddResponse = AddResponse(in.x + in.y)
}
}
And now you can define process using this type class:
def process[I](in: I)(implicit p: Processable[I]): p.Out = p(in)
Which works as desired (note the appropriate static types):
scala> val res: ToUppercaseResponse = process(ToUppercaseRequest("foo"))
res: ToUppercaseResponse = ToUppercaseResponse(FOO)
scala> val res: AddResponse = process(AddRequest(0, 1))
res: AddResponse = AddResponse(1)
But it doesn't work on arbitrary types:
scala> process("whatever")
<console>:14: error: could not find implicit value for parameter p: Processable[String]
process("whatever")
^
You don't even have to use a path dependent type (you should be able just to have two type parameters on the type class), but it makes using process a little nicer if e.g. you have to provide the type parameter explicitly.
Update: everything above assumes that you don't want to change your case class signatures (which definitely isn't necessary). If you are willing to change them, though, you can do this a little more concisely:
trait Input[Out] {
def computed: Out
}
case class AddRequest(x: Int, y: Int) extends Input[AddResponse] {
def computed: AddResponse = AddResponse(x + y)
}
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String) extends Input[ToUppercaseResponse] {
def computed: ToUppercaseResponse = ToUppercaseResponse(str.toUpperCase)
}
case class ToUppercaseResponse(upper: String)
def process[O](in: Input[O]): O = in.computed
And then:
scala> process(AddRequest(0, 1))
res9: AddResponse = AddResponse(1)
scala> process(ToUppercaseRequest("foo"))
res10: ToUppercaseResponse = ToUppercaseResponse(FOO)
Which kind of polymorphism (parametric or ad-hoc) you should prefer is entirely up to you. If you want to be able to describe a mapping between arbitrary types, use a type class. If you don't care, or actively don't want this operation to be available for arbitrary types, using subtyping.
You can define a common trait for Requests, and a common trait for Responses where the request type is defined for specific response type:
trait Request[R <: Response]
trait Response
case class AddRequest(x: Int, y: Int) extends Request[AddResponse]
case class AddResponse(sum: Int) extends Response
case class ToUppercaseRequest(str: String) extends Request[ToUppercaseResponse]
case class ToUppercaseResponse(upper: String) extends Response Response[ToUppercaseRequest]
Then, process signature would be:
def process[A <: Request[B], B <: Response](req: A): B
When you call process, you'll have to explicitly define the types so that the returned type is what you expect it to be - it can't be inferred specifically enough:
val r1: AddResponse = process[AddRequest, AddResponse](AddRequest(2, 3))
val r2: ToUppercaseResponse = process[ToUppercaseRequest, ToUppercaseResponse](ToUppercaseRequest("aaa"))
I have several case classes with a count field. It's 1 by default, and I have a reduce in my code that groups duplicates and sums that value to find the number of each object. E.g.:
case class Person(name: String, count = 1)
personList.groupBy(_.name).reduce((x,y) => x.copy(count = x.count + 1))
I have this logic in several case classes, and since my logic is a bit more complicated than the example above I want to create a generic merging function.
So I've created a sealed trait with a count field. I've then changed my case classes to extend from this, e.g.:
case class Person(name: String, override val count) extends Countable
So far, so good.
However, I can't work out how to declare my merge function so that it only accepts case classes that extend Countable. Because of that, it can't find the copy method.
Here's what I have:
def merge[T <: Countable](f: T => Seq[String])(ms: Seq[T]): Vector[T] =
ms.groupBy(x => f(x).mkString("_")).mapValues(_.reduce { (x,y) =>
x.copy(count = x.count + 1) // can't find `copy`
}).values.toVector
Is there a typeclass that I can also include that means a type has a copy method (or is a case class) using Scala 2.11.7?
Update:
Countable trait is:
sealed trait Countable {
def timesSeen: Long = 1
}
How did you defined you Countable trait.
Following snippet works fine for me:
trait Countable[Z] {
def count: Int
def copy: Z
}
case class Person(name: String, override val count: Int) extends Countable[Person] {
override def copy: Person = this
}
def merge[T <: Countable[T]](f: T => Seq[String])(ms: Seq[T]): Vector[T] = {
val r = ms.groupBy(x => f(x).mkString("_")).mapValues(_.reduce { (x, y) =>
x.copy
}).values.toVector
r
}
In Scala, it's possible to specify whether a function or class is covarient or contravarient in the following manner
class Foo[+arg] // covarient
class Bar[-arg] // contravarient
What are the real world uses of this feature?
I know the compiler runs checks to make sure that the stated entity is actually covarient or otherwise, but what is the benefit of even adding such annotations?
The simplest case where your probably already using it without knowing it is the scala collections.
class A()
class B() extends A
case class Container[T](elem : T)
val listOfA:List[A] = List[B](new B(),new B())
val containerOfA:Container[A] = Container[B](new B()) // fails
Imagine you have the following hierarchy:
class A
class B extends A
Covariance. Covariant type can be used as return type:
class Foo[+arg] { // Covariant
def getArg(): arg = ???
}
def testCovariant(): Unit = {
val fooB = new Foo[B]
val foo: Foo[A] = fooB
// returns only objects of class derived from A
// so it is safe
val a: A = foo.getArg()
}
So you can use any of Foo[DerivedClass]where Foo[BaseClass] is used, because anywhere Foo[BaseClass].getArg is called BaseClass is expected as result and any DerivedClass can be returned and assigned to it.
Contravariance. Contravariant type can be used as method parameter type:
class Bar[-arg] { // Contravariant
def setArg(p: arg): Unit = ???
}
def testContravariant(): Unit = {
val barA = new Bar[A]
val bar: Bar[B] = barA
// can set B to bar which is actually Bar[A]
// but Bar[A].setArg(p: A) can accept any object
// of type derived from A
// so it is safe
bar.setArg(new B)
}
Again. You can use any of Bar[DerivedClass] where Bar[BaseClass] is used, because anywhere Bar[DerivedClass].setArg(p: DerivedClass) is called DerivedClass is expected as argument and any Bar[BaseClass] can be used in this context, because you can always pass DerivedClass to Bar[BaseClass].setArg(p: BaseClass).
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.