Implicitly convert parameter Option[T] to T in a case class - scala

I had a case class with a option parameter, let's say:
case class Student(id: Option[Int], name: String)
To get a Student instance, not only I could use Student(Some(1), "anderson"), I also want this form to be a valid way Student(2,"Sarah")
I guess I have to create a Int => Option[Int] and put it somewhere. So what's the best way to do so?
Update
As mentioned in the comment, override apply method will block calling it by Student.apply _

It might be easier to just make an apply method in a companion object.
case class Student(id: Option[Int], name: String)
object Student {
def apply(id: Int, name: String): Student = {
Student(Some(id), name)
}
}

An alternative solution using implicit conversions:
implicit def intToOption(x: Int) = Some(x)
case class Student(id: Option[Int], name: String)
scala> Student(1,"Nu")
res1: Student = Student(Some(1),Nu)

Related

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

default case class argument depending on other arguments scala

In scala I'm not allowed to perform the following:
case class Terminal(value: Double, name: String = value.toString)
Moreover I also cannot do this:
case class Terminal(value: Double)(name: String = value.toString)
I understand the multiple parameter list approach is not supported for constructors.
Is is there a way to define in the apply method in order to make this possible?
Expected behavior:
Terminal(1.0) // => Terminal (1.0, "1.0")
You can't do this in the case class itself, and it won't make a constructor, but it is possible through the apply method on the companion.
case class Terminal(value: Double, name: String)
object Terminal {
def apply(value: Double): Terminal = Terminal(value, value.toString)
}
Note that:
def apply(value: Double, name: String = value.toString) = new Terminal(value, name)
is an error because it conflicts with the autogenerated apply.
Maybe you just want this?
case class Terminal(value: Double) {
val name = a.toString
}

type erasure and Inheritance in scala

i have following class hierarchy.
trait Item {val id: String}
case class MItem(override val id: String, val name: String) extends Item
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name)
val d = new DItem("1", "one", "another one")
println(d)
Expected Output
DItem(1, one, another one)
Actual Output
Mitem(1,one)
Why is this happening. What is recommended so that i get the real type of my object and the not type of super class.
This is not a type erasure. You are getting this result because DItem gets toString() implementation inherited from Mitem. You have to override it to get what you want
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
So here is a result:
scala> val d = new DItem("1", "one", "another one")
d: DItem = DItem(1, one, another one)
scala> println(d)
DItem(1, one, another one)
It is almost always a bad idea to inherit from case classes because besides toString successor class will also inherit equals and hashCode.
Another drawback is limited pattern-matching for such successor classes i.e it is impossible to use such classes in case branches and may lead to confusing errors.
Example
case class A(id: String)
class B(id: String, name: String) extends A(id)
new B("foo", "bar") match {
case A(id) => println(id)
case other => println(other)
}
You may expect that there is no error in this code, but you'll get
<console>:17: error: constructor cannot be instantiated to expected type;
found : A
required: B
case A(id) => println(id)
^
However if you'll infer a type for B instance explicitly it will work
scala> new B("foo", "bar").asInstanceOf[A] match {
| case A(id) => println(id)
| case other => println(other)
| }
foo
So... Inheriting from case classes is very error-prone and confusing and should be avoided unless you know what are you doing.
Inheriting from case classes is deprecated as far as I know. So case classes can (should) only inherit from regular classes.
doing println usually invoke toString on the object pass on it.
so what happen on your code is, it will invoke the toString implementation of the object, it happens to be MItem that has this implementation.
so you need to override the toString on DItem like this:
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
if you want to just get the type of the object you can use getClass.
println(d.getClass)

Transform one case class into another when the argument list is the same

I have a lot of similar case classes which mean different things but have the same argument list.
object User {
case class Create(userName:String, firstName: String, lastName: String)
case class Created(userName:String, firstName: String, lastName: String)
}
object Group {
case class Create(groupName:String, members: Int)
case class Created(groupName:String, members: Int)
}
Given this kind of a setup, I was tired of writing methods that take an argument of type Create and return an argument of type Created. I have tons of test cases that do exactly this kind of thing.
I could write a function to convert one case class into the other. This function converts User.Create into User.Created
def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create"))
I had to write another such function for Group.
What I'd really like to have is a generic function that takes the two types of the case classes and an object of one case class and converts into the other. Something like,
def transform[A,B](a: A):B
Also, this function shouldn't defeat the purpose of reducing boilerplate. Please feel free to suggest a different signature for the function if that's easier to use.
Shapeless to the rescue!
You can use Shapeless's Generic to create generic representations of case classes, that can then be used to accomplish what you're trying to do. Using LabelledGeneric we can enforce both types and parameter names.
import shapeless._
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
case class SortOfCreated(screenName: String, firstName: String, lastName: String)
val c = Create("username", "firstname", "lastname")
val createGen = LabelledGeneric[Create]
val createdGen = LabelledGeneric[Created]
val sortOfCreatedGen = LabelledGeneric[SortOfCreated]
val created: Created = createdGen.from(createGen.to(c))
sortOfCreatedGen.from(createGen.to(c)) // fails to compile
For the record, here is the simplest typesafe syntax I've managed to implement:
implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) {
def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
And it can be used like this:
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
val created = Create("foo", "bar", "baz").convertTo(Generic[Created])
Or the same thing with LabelledGeneric to achieve better type safety:
implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) {
def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created]))

Scala List and Subtypes

I want to be able to refer to a list that contains subtypes and pull elements from that list and have them implicitly casted. Example follows:
scala> sealed trait Person { def id: String }
defined trait Person
scala> case class Employee(id: String, name: String) extends Person
defined class Employee
scala> case class Student(id: String, name: String, age: Int) extends Person
defined class Student
scala> val x: List[Person] = List(Employee("1", "Jon"), Student("2", "Jack", 23))
x: List[Person] = List(Employee(1,Jon), Student(2,Jack,23))
scala> x(0).name
<console>:14: error: value name is not a member of Person
x(0).name
^
I know that x(0).asInstanceOf[Employee].name but I was hoping there was a more elegant way with types. Thanks in advance.
The best way is to use pattern matching. Because you are using a sealed trait the match will be exhaustive which is nice.
x(0) match {
case Employee(id, name) => ...
case Student(id, name, age) => ...
}
Well, if you want the employees, you could always use a collect:
val employees = x collect { case employee: Employee => employee }