Scala: collection of similar yet different types - scala

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))

Related

How can I vary the type parameter when dealing with a collection?

Say I have a case class that has a type parameter T:
case class ProdutOption[T](name: String, value:T)
And this case class is contained in another case class like:
case class Product[T](id: String, options: Set[ProductOption[T]])
What is the correct way to model this?
I want my options parameter to be able to have different types of T... I don't want all items in the set to be forced to a specific type.
Is this scenario possible to model using type parameters like this?
Everything depends on whether your set of possible option types is closed (i.e. known statically) or open (unknown until runtime) and how exactly do you want to process these options.
If you can enumerate all possible options, use an ADT (algebraic data type):
sealed trait ProductOption[+T] {
def name: String
def value: T
}
object ProjectOption {
case class SomeNumericOption(value: Int) extends ProductOption[Int] {
def name: String = "numeric"
}
case class SomeTextualOption(value: String) extends ProductOption[String] {
def name: String = "textual"
}
}
case class Product(id: String, option: Set[ProductOption[_]])
You can then process these options in a type-safe way using pattern matching.
If your option hierarchy is not known then you should introduce some method for processing this option into the base trait, which will no longer be sealed:
trait ProductOption {
def name: String
def processMe(): Whatever = ...
}
case class Product(id: String, option: Set[ProductOption])
What exactly would processMe do depends on your use case.
This is a classical example of so called expression problem.
It's a little unclear what exactly you're looking for, but it may be useful to consider covariance for ProductOption:
case class ProductOption[+T](name: String, value: T)
This says that if S is a supertype of T, ProductOption[T] is a subtype of ProductOption[S].
Specifically this then means that a Product[Any] allows options to be any kind of ProductOption:
Product("my favorite product", Set(ProductOption[String]("color", "blue"), ProductOption[ShirtSize]("size", ShirtSize.TwoXL)) // type is Product[AnyRef]

scala refactor 2 almost identical case classes

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").

How to convert between to case classes with `mostly the same` fields using Scala Shapeless

Here I have to case classes which have mostly the same fields.
final case class Id(id: String) // Param Class
final case class Age(id: Id, age: Int) // Param Class
final case class A(id: Id, data: Map[String, Any], age: Age) extends Presentable[A, APre] // Main Class 1
final case class APre(id: String, data: Map[String, Any], age: Int) // Main Class 2
Here A and APre are my main classes.
Now I want to convert between this two class using Shapeless, so I write the following pseudo function:
trait Presentable[E, P] {
def makePresentation[ET <: HList, PT <: HList](entity: E)(func : ET => PT)(implicit entGen: LabelledGeneric.Aux[E, ET], preGen: LabelledGeneric.Aux[P, PT]): P = {
val entList = entGen.to(entity)
preGen.from(func(entList))
}
}
Here func is a mapper mapping the HList of A to HList of APre (or vice versa).
And I want to use the function like this:
val age = Age(Id("age_1"), 18)
val a = A(Id("id"), Map("tag1" -> "value1", "tag2" -> "value2"), age)
val pre = a.makePresentation { entList =>
entList.updateWith('id)((id: Id) => id.id).updateWith('age)((a: Age) => a.age)
}
Here I can imply the mapping function myself. So I can convert any two case classes
So questions are:
1. How can I convert this two classes using shapeless?
2. In fact, I have tons of pairs of class like A to APre. So I want a trait to extract this convert function using generic. How to write this function?
Disclaimer: I'm one of chimney's authors.
In earlier releases of chimney we implemented exactly what you are asking about - conversion between mostly identical case classes using shapeless.
I wouldn't recommend writing it by hand as there are some corner cases to consider (creating values if new object is missing some, transforming the fields that changed type/name, Java Beans, value classes, etc) and then you have to come up with how would you configure it, so if you need to have shapeless-bases solution look at the code from 0.1.10.
However, since 0.2.0, we rewritten the implementation into macros since if you had bigger case classes to transform (e.g. 12 fields or more) some derivations could compute several minutes(!) with no hope of improvement unless we dropped some of cases we support.
If you're just looking for a way of handling your transformations, then use newest chimney and call it a day.

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

How to override apply in a case class companion

So here's the situation. I want to define a case class like so:
case class A(val s: String)
and I want to define an object to ensure that when I create instances of the class, the value for 's' is always uppercase, like so:
object A {
def apply(s: String) = new A(s.toUpperCase)
}
However, this doesn't work since Scala is complaining that the apply(s: String) method is defined twice. I understand that the case class syntax will automatically define it for me, but isn't there another way I can achieve this? I'd like to stick with the case class since I want to use it for pattern matching.
The reason for the conflict is that the case class provides the exact same apply() method (same signature).
First of all I would like to suggest you use require:
case class A(s: String) {
require(! s.toCharArray.exists( _.isLower ), "Bad string: "+ s)
}
This will throw an Exception if the user tries to create an instance where s includes lower case chars. This is a good use of case classes, since what you put into the constructor also is what you get out when you use pattern matching (match).
If this is not what you want, then I would make the constructor private and force the users to only use the apply method:
class A private (val s: String) {
}
object A {
def apply(s: String): A = new A(s.toUpperCase)
}
As you see, A is no longer a case class. I am not sure if case classes with immutable fields are meant for modification of the incoming values, since the name "case class" implies it should be possible to extract the (unmodified) constructor arguments using match.
UPDATE 2016/02/25:
While the answer I wrote below remains sufficient, it's worth also referencing another related answer to this regarding the case class's companion object. Namely, how does one exactly reproduce the compiler generated implicit companion object which occurs when one only defines the case class itself. For me, it turned out to be counter intuitive.
Summary:
You can alter the value of a case class parameter before it is stored in the case class pretty simply while it still remaining a valid(ated) ADT (Abstract Data Type). While the solution was relatively simple, discovering the details was quite a bit more challenging.
Details:
If you want to ensure only valid instances of your case class can ever be instantiated which is an essential assumption behind an ADT (Abstract Data Type), there are a number of things you must do.
For example, a compiler generated copy method is provided by default on a case class. So, even if you were very careful to ensure only instances were created via the explicit companion object's apply method which guaranteed they could only ever contain upper case values, the following code would produce a case class instance with a lower case value:
val a1 = A("Hi There") //contains "HI THERE"
val a2 = a1.copy(s = "gotcha") //contains "gotcha"
Additionally, case classes implement java.io.Serializable. This means that your careful strategy to only have upper case instances can be subverted with a simple text editor and deserialization.
So, for all the various ways your case class can be used (benevolently and/or malevolently), here are the actions you must take:
For your explicit companion object:
Create it using exactly the same name as your case class
This has access to the case class's private parts
Create an apply method with exactly the same signature as the primary constructor for your case class
This will successfully compile once step 2.1 is completed
Provide an implementation obtaining an instance of the case class using the new operator and providing an empty implementation {}
This will now instantiate the case class strictly on your terms
The empty implementation {} must be provided because the case class is declared abstract (see step 2.1)
For your case class:
Declare it abstract
Prevents the Scala compiler from generating an apply method in the companion object which is what was causing the "method is defined twice..." compilation error (step 1.2 above)
Mark the primary constructor as private[A]
The primary constructor is now only available to the case class itself and to its companion object (the one we defined above in step 1.1)
Create a readResolve method
Provide an implementation using the apply method (step 1.2 above)
Create a copy method
Define it to have exactly the same signature as the case class's primary constructor
For each parameter, add a default value using the same parameter name (ex: s: String = s)
Provide an implementation using the apply method (step 1.2 below)
Here's your code modified with the above actions:
object A {
def apply(s: String, i: Int): A =
new A(s.toUpperCase, i) {} //abstract class implementation intentionally empty
}
abstract case class A private[A] (s: String, i: Int) {
private def readResolve(): Object = //to ensure validation and possible singleton-ness, must override readResolve to use explicit companion object apply method
A.apply(s, i)
def copy(s: String = s, i: Int = i): A =
A.apply(s, i)
}
And here's your code after implementing the require (suggested in the #ollekullberg answer) and also identifying the ideal place to put any sort of caching:
object A {
def apply(s: String, i: Int): A = {
require(s.forall(_.isUpper), s"Bad String: $s")
//TODO: Insert normal instance caching mechanism here
new A(s, i) {} //abstract class implementation intentionally empty
}
}
abstract case class A private[A] (s: String, i: Int) {
private def readResolve(): Object = //to ensure validation and possible singleton-ness, must override readResolve to use explicit companion object apply method
A.apply(s, i)
def copy(s: String = s, i: Int = i): A =
A.apply(s, i)
}
And this version is more secure/robust if this code will be used via Java interop (hides the case class as an implementation and creates a final class which prevents derivations):
object A {
private[A] abstract case class AImpl private[A] (s: String, i: Int)
def apply(s: String, i: Int): A = {
require(s.forall(_.isUpper), s"Bad String: $s")
//TODO: Insert normal instance caching mechanism here
new A(s, i)
}
}
final class A private[A] (s: String, i: Int) extends A.AImpl(s, i) {
private def readResolve(): Object = //to ensure validation and possible singleton-ness, must override readResolve to use explicit companion object apply method
A.apply(s, i)
def copy(s: String = s, i: Int = i): A =
A.apply(s, i)
}
While this directly answers your question, there are even more ways to expand this pathway around case classes beyond instance caching. For my own project needs, I have created an even more expansive solution which I have documented on CodeReview (a StackOverflow sister site). If you end up looking it over, using or leveraging my solution, please consider leaving me feedback, suggestions or questions and within reason, I will do my best to respond within a day.
I don't know how to override the apply method in the companion object (if that is even possible) but you could also use a special type for upper case strings:
class UpperCaseString(s: String) extends Proxy {
val self: String = s.toUpperCase
}
implicit def stringToUpperCaseString(s: String) = new UpperCaseString(s)
implicit def upperCaseStringToString(s: UpperCaseString) = s.self
case class A(val s: UpperCaseString)
println(A("hello"))
The above code outputs:
A(HELLO)
You should also have a look at this question and it's answers: Scala: is it possible to override default case class constructor?
For the people reading this after April 2017: As of Scala 2.12.2+, Scala allows overriding apply and unapply by default. You can get this behavior by giving -Xsource:2.12 option to the compiler on Scala 2.11.11+ as well.
It works with var variables:
case class A(var s: String) {
// Conversion
s = s.toUpperCase
}
This practice is apparently encouraged in case classes instead of defining another constructor. See here.. When copying an object, you also keep the same modifications.
Another idea while keeping case class and having no implicit defs or another constructor is to make the signature of apply slightly different but from a user perspective the same.
Somewhere I have seen the implicit trick, but can´t remember/find which implicit argument it was, so I chose Boolean here. If someone can help me out and finish the trick...
object A {
def apply(s: String)(implicit ev: Boolean) = new A(s.toLowerCase)
}
case class A(s: String)
I faced the same problem and this solution is ok for me:
sealed trait A {
def s:String
}
object A {
private case class AImpl(s:String)
def apply(s:String):A = AImpl(s.toUpperCase)
}
And, if any method is needed, just define it in the trait and override it in the case class.
If you're stuck with older scala where you cant override by default or you dont want to add the compiler flag as #mehmet-emre showed, and you require a case class, you can do the following:
case class A(private val _s: String) {
val s = _s.toUpperCase
}
As of 2020 on Scala 2.13, the above scenario of overriding a case class apply method with same signature works totally fine.
case class A(val s: String)
object A {
def apply(s: String) = new A(s.toUpperCase)
}
the above snippet compiles and runs just fine in Scala 2.13 both in REPL & non-REPL modes.
I think this works exactly how you want it to already. Here's my REPL session:
scala> case class A(val s: String)
defined class A
scala> object A {
| def apply(s: String) = new A(s.toUpperCase)
| }
defined module A
scala> A("hello")
res0: A = A(HELLO)
This is using Scala 2.8.1.final