scala refactor 2 almost identical case classes - scala

I have the following case classes in scala:
case class Brand(brand: String, country: String)
object Brand {
def func(data: (String, String)): Brand = {}
}
case class Manufacturer(manufacturer: String, country: String)
object Manufacturer {
def func(data: (String, String)): Manufacturer = {}
}
As you can see, the problem here is that Brand and Manufacturer actually have the same structure, but they share different field names. So the same functions will need to be implemented twice, just with different output type. The logic is the same.
Is there a good way to refactor this code?
I prefer not to combine them into one case class such as
case class Attribute(attribute: String, country: String)
Thanks.

You can use Brand.tupled instead of Brand.func, this removes the need for this function.
tupled is a method generated for case classes which takes a tuple as input and create an instance of the case class.
For instance, Brand.tupled(("a","b")) will give Brand("a","b").

Related

Case class immutable still able to change the parameter values

I reviewed some code from a colleague and I came across a case class which is by default immutable.
the below case class can be changed so my question is how is this possible since case classes are immutable but in this construct i can change the case class parameters?
case class RegisterCustomerRequest(`first-name`: String,
`last-name`: String,
`house-details`: String,
street: String,
zipcode: String,
city: String
extends WcRequestData {
def this(cardHolderData: CardHolderData,
registrationCode: RegistrationCode,
customerNumber: Long,
cardDesignImageId: String) =
this(`first-name` = cardHolderData.firstname,
`last-name` = cardHolderData.lastname,
street = cardHolderData.streetAndNumber,
zipcode = cardHolderData.zipCode,
city = cardHolderData.city,
# `house-details` =
s"${if (cardHolderData.employerName.contains("&"))
cardHolderData.employerName.replace("&" , " & ") else " /
"}${cardHolderData.employerName} ")#
}
why can I define a def this method which can change the values of parameters. What is this construct good for is this good coding style?
The case class RegisterCustomerRequest is still immutable however it has an auxiliary constructor def this which allows it to be constructed in a different way. For example, given
case class User(name: String)
case class Foo(name: String) {
def this(user: User) {
this(name = user.name)
}
}
we can construct Foo like so
Foo("picard")
or using the auxiliary constructor
new Foo(User("picard"))
In both cases the result is an immutable object. To confirm immutability try reassigning name after construction
(new Foo(User("picard"))).name = "worf" // Error: reassignment to val
As suggested by som-snytt, we can define apply method on companion object instead of auxiliary constructor like so
object Foo {
def apply(user: User): Foo = Foo(user.name)
}
which enables the following construction
Foo(User("picard"))

Scala - Domain Objects with lots of fields

This might be a stupid question but I am relatively new to Scala so please bear with me. I am trying to model a domain object for a Spark job in Scala, which reflects the data structure of the source record and contains more than 100 fields. I am trying to figure out the best way to model this as I don't feel comfortable simply adding all the fields to a single case class. I thought about grouping closely associated fields into nested case classes but then I read in a few places that nesting case classes is not recommended. I would appreciate some input on what would be the best approach.
Edit: In response to Alvaro's comments:
So in essence we are saying that this is not recommended:
case class Product(name: String,
desc: String,
productGroup: String) {
case class ProductPack(packType: String,
packQuantity: Int,
packQuantityUnit: String,
packUnitPrice: Float)
}
While this would be fine:
case class Product(name: String,
desc: String,
productGroup: String,
productPack: ProductPack) {
}
case class ProductPack(packType: String,
packQuantity: Int,
packQuantityUnit: String,
packUnitPrice: Float) {
}
Your update is correct.
Another alternative: If a case class mostly makes sense in the context of another concept, sometimes I define the case class inside a companion to the concept:
case class Product(
name: String,
desc: String,
productGroup: String
productPack: Product.Pack
)
object Product {
case class Pack(
packType: String,
packQuantity: Int,
packQuantityUnit: String,
packUnitPrice: Float
)
}
That should also be fine. The class is contained in an object, but it is not "nested" in the Product class.

How to define implicit Writes in trait

I have multiple case classes representing values in DB for ex User which saves user based properties like name / age / address and CallLog which saves timestamp / status_of_call
What i want to achieve
I want to have a helper function which accepts list of models and checks if the list is empty then returns "error" otherwise should return json array of the list.
My Approach
I want to have a trait which groups certain models in it and the helper method will accept either the trait or List of it in order to check or may be have a generic which implements the trait.
Problem
Since implicit writes are tightly coupled with the model class, compiler throws the error on the line Json.toJson(list)
Things i have tried
Kept implicit in trait and got recursive type error
I am scala noob pardon me if this sounds silly
Thanks in advance
Since User, CallLog, etc. will be serialized differently, Each Writes[T] will be different for each implementation of your Model trait, so a Writes[Model] has to know about the implementation it is trying to serialize.
It is therefore not possible to have it part of the Model trait, because this information isn't known yet when you define it.
A workaround in your case would be to define your Writes[Model] in the scope of your helper function instead.
An implementation of your helper function could be like this :
import play.api.libs.json.{JsValue, Json, Writes}
sealed trait Model
case class User(name: String, age: String, address: String) extends Model
object User {
implicit val userWrites = Json.writes[User]
}
case class CallLog(timestamp: String, status_of_call: String) extends Model
object CallLog {
implicit val callLogWrites = Json.writes[CallLog]
}
implicit val modelWrites = new Writes[Model] {
override def writes(o: Model): JsValue = o match {
case u: User => Json.toJson(u)
case cl: CallLog => Json.toJson(cl)
}
}
def helper(models: Model*): Either[JsValue, String] = models match {
case Nil => Right("Error")
case _ => Left(Json.toJson(models))
}
helper(User("John", "32", "..."))
helper(User("John", "32", "..."), CallLog("now", "In progress"))

Scala: collection of similar yet different types

What I want to to is to keep similar yet different types in the same collection. Currently, I'm doing this using polymorphism (code is simplified):
trait Item
case class DoubleItem(id: String, value: Double) extends Item
case class StringItem(id: String, value: String) extends Item
case class BooleanItem(id: String, value: Boolean) extends Item
Then it's possible to create a Seq[Item] and add instances of the three types to it.
What I don't like is the redundancy. Usually I would use a generic Item[A], but from my point of understanding, this eliminates the possibility of using a single collection (since A in Seq[Item[A]] has to be a concrete type).
Is there a better approach?
(Btw: I want to avoid using an HList implementation or something similar that increases complexity).
Since Item is covariant in value, you might do this:
case class Item[+A](id: String, value: A)
// example usage
val seq: Seq[Item[Any]] = Seq(Item("foo", 1), Item("bar", true))
def findBoolean(in: Seq[Item[Any]]): Option[Boolean] = in.collectFirst {
case Item(_, b: Boolean) => b
}
assert(findBoolean(seq) == Some(true))

Scala case class inheritance

I have an application based on Squeryl. I define my models as case classes, mostly since I find convenient to have copy methods.
I have two models that are strictly related. The fields are the same, many operations are in common, and they are to be stored in the same DB table. But there is some behaviour that only makes sense in one of the two cases, or that makes sense in both cases but is different.
Until now I only have used a single case class, with a flag that distinguishes the type of the model, and all methods that differ based on the type of the model start with an if. This is annoying and not quite type safe.
What I would like to do is factor the common behaviour and fields in an ancestor case class and have the two actual models inherit from it. But, as far as I understand, inheriting from case classes is frowned upon in Scala, and is even prohibited if the subclass is itself a case class (not my case).
What are the problems and pitfalls I should be aware in inheriting from a case class? Does it make sense in my case to do so?
My preferred way of avoiding case class inheritance without code duplication is somewhat obvious: create a common (abstract) base class:
abstract class Person {
def name: String
def age: Int
// address and other properties
// methods (ideally only accessors since it is a case class)
}
case class Employer(val name: String, val age: Int, val taxno: Int)
extends Person
case class Employee(val name: String, val age: Int, val salary: Int)
extends Person
If you want to be more fine-grained, group the properties into individual traits:
trait Identifiable { def name: String }
trait Locatable { def address: String }
// trait Ages { def age: Int }
case class Employer(val name: String, val address: String, val taxno: Int)
extends Identifiable
with Locatable
case class Employee(val name: String, val address: String, val salary: Int)
extends Identifiable
with Locatable
Since this is an interesting topic to many, let me shed some light here.
You could go with the following approach:
// You can mark it as 'sealed'. Explained later.
sealed trait Person {
def name: String
}
case class Employee(
override val name: String,
salary: Int
) extends Person
case class Tourist(
override val name: String,
bored: Boolean
) extends Person
Yes, you have to duplicate the fields. If you don't, it simply would not be possible to implement correct equality among other problems.
However, you don't need to duplicate methods/functions.
If the duplication of a few properties is that much of an importance to you, then use regular classes, but remember that they don't fit FP well.
Alternatively, you could use composition instead of inheritance:
case class Employee(
person: Person,
salary: Int
)
// In code:
val employee = ...
println(employee.person.name)
Composition is a valid and a sound strategy that you should consider as well.
And in case you wonder what a sealed trait means — it is something that can be extended only in the same file. That is, the two case classes above have to be in the same file. This allows for exhaustive compiler checks:
val x = Employee(name = "Jack", salary = 50000)
x match {
case Employee(name) => println(s"I'm $name!")
}
Gives an error:
warning: match is not exhaustive!
missing combination Tourist
Which is really useful. Now you won't forget to deal with the other types of Persons (people). This is essentially what the Option class in Scala does.
If that does not matter to you, then you could make it non-sealed and throw the case classes into their own files. And perhaps go with composition.
case classes are perfect for value objects, i.e. objects that don't change any properties and can be compared with equals.
But implementing equals in the presence of inheritance is rather complicated. Consider a two classes:
class Point(x : Int, y : Int)
and
class ColoredPoint( x : Int, y : Int, c : Color) extends Point
So according to the definition the ColorPoint(1,4,red) should be equal to the Point(1,4) they are the same Point after all. So ColorPoint(1,4,blue) should also be equal to Point(1,4), right? But of course ColorPoint(1,4,red) should not equal ColorPoint(1,4,blue), because they have different colors. There you go, one basic property of the equality relation is broken.
update
You can use inheritance from traits solving lots of problems as described in another answer. An even more flexible alternative is often to use type classes. See What are type classes in Scala useful for? or http://www.youtube.com/watch?v=sVMES4RZF-8
In these situations I tend to use composition instead of inheritance i.e.
sealed trait IVehicle // tagging trait
case class Vehicle(color: String) extends IVehicle
case class Car(vehicle: Vehicle, doors: Int) extends IVehicle
val vehicle: IVehicle = ...
vehicle match {
case Car(Vehicle(color), doors) => println(s"$color car with $doors doors")
case Vehicle(color) => println(s"$color vehicle")
}
Obviously you can use a more sophisticated hierarchy and matches but hopefully this gives you an idea. The key is to take advantage of the nested extractors that case classes provide