Scala: Case classes with shared parameter? - scala

I don't know if there's a way to do this, but I want to be able to specify that a set of case classes all share a parameter (so I can access it without pattern matching).
EDIT: After some help I have
abstract class Test(val termName : String)
case class TestOne(value : Int, name : String = null) extends Test(name)
case class TesTwo(otherValue : Boolean, name : String = null) extends Test(name)
Which is almost exactly what I want, though it would be nice to get rid of the ugliness of having both name and termName.

though it would be nice to get rid of the ugliness of having both name and termName.
Just make it a normal member of the supertype (which can easily be a trait), not a constructor parameter:
trait Test {
val name : String
}
case class TestOne(value : Int, name : String = null) extends Test
case class TestTwo(otherValue : Boolean, name : String = null) extends Test
(The standard Scala style is name: String, no space before :.)

If every implementation should have a separate, distinct implementation, declare abstract member and implement it in children:
abstract trait NamedCaseClass {
def name: String
}
case class OneCase(number : Int) extends NamedCaseClass {
override def name: String = "OneCase"
}
case class OtherCase(truth : Boolean) extends NamedCaseClass {
override def name: String = "OtherCase"
}
If all implementations could just use some value that you can calculate on creation, you can use abstract class constructor:
abstract class NamedCaseClass(val name: String)
case class OneCase(number : Int) extends NamedCaseClass("OneCase")
case class OtherCase(truth : Boolean) extends NamedCaseClass("OtherCase")
If you have a sealed hierarchy you could also define it like this:
sealed trait NamedCaseClass {
def nameOpt: Option[String] = this match {
case OneCase(number) => Some(name.toString)
case OtherCase(_) => None
}
}
case class OneCase(number : Int) extends NamedCaseClass
case class OtherCase(truth : Boolean) extends NamedCaseClass
Which one is more convenient pretty much depend on if you have a sealed hierarchy or open, if you can calculate value on creation or is this something more complicated that would work better as a method.

Related

when do I need to add member definitions to my class in scala?

Im kinda new to scala, and I came up with this code when trying to learn match case syntax, the first code is what I did with its error, and the second is how I fixed it.
does anybody have a clue why do I need to add member "name" to my class?
trait T
class A (name: String) extends T
case class B (cls: A)
object Main {
def main(args: Array[String]) {
val a: A = new A("John")
val b: B = new B(a)
checkType(b)
}
def checkType(cls: AnyRef) {
cls match {
case B(input) => println("your name is " + input.name)
case _ => println("others")
}
}
}
the above code gives me the error :"value name is not a member of A"
now if i change the class A to this:
class A (n: String) extends T
{
var name: String = n
}
the output will be "your name is John" (which is what I would expect)
P.S: if I change class A to case class, the problem is solved, why tho?
class A (name: String) extends T
parameters without val or var are of type private [this] and are visible only within the class
if you change it to
class A (val name: String) extends T
this makes constructor parameter name public and can be accessible with class objects without need of a getter (use var if you want to change the values)
and if its this
case class A (name: String) extends T
case class parameters are public val by default so thats why it worked as you said
reference-
https://docs.scala-lang.org/tour/classes.html
https://docs.scala-lang.org/tour/case-classes.html

Extend case class from another case class

I have two case class Person and Employee
case class Person(identifier: String) {}
case class Employee (salary: Long) extends Person {}
I am getting following error:
Unspecified value parameters: identifier: String
Error: case class Employee has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes
I am new to Scala and not able to understand what I have to do.
Version:
Scala : 2.11
Unfortunately, I'm afraid it is not possible for case class to extend another case class.
The inheritance in "plain" classes would look like:
class Person(val identifier: String) {}
class Employee(override val identifier: String, salary: Long)
extends Person(identifier) {}
val el = new Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
If you would like to achieve a similar effect with case classes, you would need to reach out to traits:
trait Identifiable {
def identifier: String
}
case class Person(identifier: String) extends Identifiable {}
case class Employee(identifier: String, salary: Long)
extends Identifiable {}
val el = Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
Defining extractors
Extractor provides a way for defining a matching statement used in pattern matching. It is defined in an object in unaply method.
Let's consider the first example again adding support for extractors:
class Person(val identifier: String)
class Employee(override val identifier: String, val salary: Long)
extends Person(identifier)
object Person {
def unapply(identifier: String): Option[Person] = {
if (identifier.startsWith("PER-")) {
Some(new Person(identifier))
}
else {
None
}
}
}
object Employee {
def unapply(identifier: String): Option[Employee] = {
if (identifier.startsWith("EMP-")) {
Some(new Employee(identifier, 999))
}
else {
None
}
}
}
Now, let's define a method that will define pattern matching using those extractors:
def process(anInput: String): Unit = {
anInput match {
case Employee(anEmployee) => println(s"Employee identified ${anEmployee.identifier}, $$${anEmployee.salary}")
case Person(aPerson) => println(s"Person identified ${aPerson.identifier}")
case _ => println("Was unable to identify anyone...")
}
}
process("PER-123-test") // => Person identified PER-123-test
process("EMP-321-test") // => Employee identified EMP-321-test, $999
process("Foo-Bar-Test") // => Was unable to identify anyone...
Case classes in Scala add several different features but often you really use only some of them. So the main question you need to answer is which features you really need. Here is a list based on the spec:
remove the need to type val before field names/constructor params
remove the need for new by adding apply method to the companion object
support for pattern matching by adding unapply method to the companion object. (One of nice things of Scala is that pattern-matching is done in a non-magical way, you can implement it for any data type without requiring it to be a case class)
add equals and hashCode implementations based on all the fields
add toString implementations
add copy method (useful because case classes are immutable by default)
implement Product trait
A reasonable guess of the equivalent for case class Person(identifier: String) is
class Person(val identifier: String) extends Product {
def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
override def equals(other: Any): Boolean = other match {
case that: Person => (that canEqual this) && identifier == that.identifier
case _ => false
}
override def hashCode(): Int = identifier.hashCode
override def toString = s"Person($identifier)"
def copy(newIdentifier: String): Person = new Person(newIdentifier)
override def productElement(n: Int): Any = n match {
case 0 => identifier
case _ => throw new IndexOutOfBoundsException(s"Index $n is out of range")
}
override def productArity: Int = 1
}
object Person {
def apply(identifier: String): Person = new Person(identifier)
def unapply(person: Person): Option[String] = if (person eq null) None else Some(person.identifier)
}
case class Employee(override val identifier: String, salary: Long) extends Person(identifier) {}
Actually the main objections to inheriting from a case class and especially making a case class inheriting another one are the Product trait, copy and equals/hashCode because they introduce ambiguity. Adding canEqual partially mitigates the last problem but not the first ones. On the other hand in a hierarchy like yours, you probably don't need the copy method or Product implementation at all. If you don't use Person in pattern matching, you don't need unapply as well. Most probably all you really need case for is apply, toString and hashCode/equals/canEqual.
Inheriting from case classes (even with regular non-case classes, which is not prohibited) is a bad idea. Check this answer out to get an idea why.
You Person does not need to be a case class. It actually does not need to be a class at all:
trait Person {
def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person

Scala Multiple Nested Case Clases

Good day everyone, I create the following case class on SCALA:
sealed abstract class Value;
case class U(name: String) extends Value
case class L(name: String) extends Value
case class B(name: String) extends Value
sealed abstract class Term
case class Var(name: String) extends Term //variable name
case class Val(value: Value) extends Term //value
sealed abstract class Pattern //patterns
case class BGP(subject: Term, predicate: Term, obj: Term) extends Pattern
case class And( pat1: Pattern, pat2: Pattern) extends Pattern
case class Filter(pred: Predicate, pattern: Pattern ) extends Pattern
def function(p: Pattern): Unit = p match {
case BGP(Var(x), Val(y), Val(z)) => {
val con:conv = new conv()
val valor:Value = Val(y).value
}
Then, as you can see, BGP contains Term and extends to pattern, Val contains Values and extends to Term, and U,L,B contains Strings and extends to Value,
In my function I want to access to the strings that contains the U or L or B case classes, the variable valor = Val(y).value contains a U class for example, but when I write valor.XXXX don't appear me the name option. The big question is How can I do to accesss to the String name from U?
You just define it on Value which btw could be a trait.
sealed trait Value {
def name: String
}
case class U(name: String) extends Value
case class L(name: String) extends Value
case class B(name: String) extends Value

Overload constructor with different param type

I know that we can overload class constructor in Scala as follows-
class Foo(x: Int, z: String) {
def this(z: String) = this(0, z);
}
But how can I overload a class which have two completely different types of parameters as below (imagine that I can identify an user either by name or numeric id)
class User(userId: Int) {
...
}
class User(userName: String) {
...
}
(imagine that I can identify an user either by name or numeric id)
You almost certainly don't want to do this by having optional fields in your class. Rather, you should encode the fact that the user is identified in various ways into the types and structure of your program.
One way to do this is to encode the user identifier using Scala's built-in Either type:
class User private(identifier : Either[String, Int]) {
def this(id : Int) = this(Right(id))
def this(name : String) = this(Left(name))
}
However, you might also want to make the nature of the user identifier a little more explicit, and encode it as your own Algebraic data type:
trait UserIdentifier
object UserIdentifier {
case class ById(id : Int) extends UserIdentifier
case class ByName(name : String) extends UserIdentifier
}
class User(id : UserIdentifier) {
def this(id : Int) = this(UserIdentifier.ById(id))
def this(name : String) = this(UserIdentifier.ByName(name))
}
By doing it this way, you prevent problems such as somebody trying to look up a name on a User which is identified by an id instead. The second approach also allows you to extend the idea of a UserIdentifier in the future, in case a user can be identified by some other construct.
Alternativelly, you can do this
object User {
def apply(userId: Int) = new UserI(userId)
def apply(name: String) = new UserS(name)
class UserI(userId: Int)
class UserS(userName: String)
}
And use it this way:
val u1 = User(1)
val u2 = User("a")
If you have a lot of common logic you can put it into a common abstract class
object User {
def apply(userId: Int) = new UserI(userId)
def apply(name: String) = new UserS(name)
class UserI(userId: Int) extends AUser
class UserS(userName: String) extends AUser
abstract class AUser{
// common logic for both classes
}
}
You can do this:
class User private() {
def this( userName: String ) = { this(); ??? }
def this( userId: Int ) = { this(); ??? }
}
the private keyword makes the no-arg constructor private. This means your other secondary constructors don't need to pass anything to the primary constructor
(effectively making the two secondary constructors independant), but the callers still cannot instantiate the class withouth passing any parameter.
Note that this pattern can be tricky to use when your class has vals to initialize from the construtors parameters.

self annotation using abstract type

I would like to define an abstract recursive data structure with an abstract type.
Something like this :
case class ParentA( name : String, children : List[ParentA] ) extends Parent {
type PARENT = ParentA
}
case class ParentB( name : String, children : List[ParentB] ) extends Parent {
type PARENT = ParentB
}
sealed abstract class Parent {
// we'd like to Define Parent as a PARENT
// something like:
// this : PARENT =>
type PARENT <: Parent
val name : String
val children : List[PARENT]
def findParent(name:String) : Option[PARENT] = {
if( name == this.name ) {
Some(this) // ouch
} else {
// re-ouch
children.flatMap( f => f.findParent(name) )
}
}
}
val a2a : ParentA = ParentA("a",List(ParentA("a1",Nil),ParentA("a2",List(ParentA("a2a",Nil))))).findParent("a2a").get
Of course this won't compile, because the compiler cannot guess that Parent.this is a PARENT .
error: type mismatch;
found : Parent.this.type (with underlying type this.Parent)
required: Parent.this.PARENT
Some(this)
And also
error: type mismatch;
found : List[Parent.this.PARENT#PARENT]
required: Option[Parent.this.PARENT]
children.flatMap( f => f.findParent(name) )
I can get around it by casting here and there but it would be better to be able to tell the compiler that Parent is a PARENT.
Or maybe i'm missing something :)
Edit - Generics
I forgot to mention that Generics are not an option. This example is actually a simplified version of a more subtle problem. Using generics will lead to a quadratic growth of the program. Here is a link explaining why generics are not always a viable alternative : Scala: Abstract types vs generics.
Basically i'm better off using abstract types - or even neither abstract types nor generics - and casting.
The same idea as mentioned by #Debilski, but no extra type parameter:
case class ParentA(name: String, children: List[ParentA]) extends Parent[ParentA]
case class ParentB(name: String, children: List[ParentB]) extends Parent[ParentB]
sealed abstract class Parent[P <: Parent[P]] { this: P =>
def name: String
def children: List[P]
def findParent(name: String): Option[P] =
if (name == this.name) Some(this)
else children.flatMap(_.findParent(name)).headOption
}
By the way, use defs instead of vals for abstract members. They allow more flexibility while implementing them in subclasses.
Only for reference. sschaef has a better way to do it.
This compiles using the Scala CRTP and self-types.
case class ParentA(name : String, children : List[ParentA]) extends Parent[ParentA]
case class ParentB(name : String, children : List[ParentB]) extends Parent[ParentB]
sealed abstract class Parent[T] { this : T =>
type PARENT = Parent[T] with T
val name : String
val children : List[PARENT]
def findParent(name:String) : Option[PARENT] = {
if( name == this.name ) {
Some(this)
} else {
children.flatMap( f => f.findParent(name) ).headOption
}
}
}
val a2a : ParentA = ParentA("a",List(ParentA("a1",Nil),ParentA("a2",List(ParentA("a2a",Nil))))).findParent("a2a").get