Doobie Query for Entities with Value Class Fields - scala

I am using Doobie to manage database persistence. I use value class fields in my entity Foo i.e.
case class CreatedOn(value: LocalDateTime) extends AnyVal
case class Resource(value: String) extends AnyVal
case class Foo(id: Option[Int], resource: Resource, createdOn: CreatedOn)
implicit fooRead: Read[Foo] = Read[(Option[Int], String, LocalDateTime)].map {
case (oid, resource, createdOn) => Foo(oid, Resource(resource), CreatedOn(createdOn))
}
implicit fooWrite: Write[Foo] = Write[(Option[Int], String, LocalDateTime)].contramap {e => (e.oid, e.resource.value, e.createdOn.value}
Yet the compiler complains missing Read[(Int, String, LocalDateTime)].map {...
Any suggestions on how I can fix this? Is using value class a bad idea, to begin with, when comes to the entity? Thanks

Doobie is able to read queries into case classes. If you imagine case classes as tuples and you imagine that you can flatten them then this is what Doobie does when you query things into a case class.
Foo(Some(1), Resource("test"), CreatedOn(time))
(Some(1), Tuple1("test"), Tuple1(time))
(Some(1), "test", time) // (Option[Int], String, LocalDateTime)
If your derivation fails you can check for which of these fields:
sql"".query[Int]
sql"".query[String]
sql"".query[LocalDateTime]
The line which fail compilation tells you the missing instance.
From my experience it is time instance. You have to import them separately, which you probably didn't import
doobie.implicits.javatime._

Related

are there advantages for using value class (without methods) vs type alias?

Let's say I have this ADT :
case class Person(id: String)
case class Kid(id: String, name: String)
I would like to represent the id field in more explicit and type safe way. I have two options
1. type alias
type PersonId = String
case class Person(id: PersonId)
case class Kid(id: String, name: PersonId)
2. value class
case class PersonId(id: String) extends AnyVal
case class Person(id: PersonId)
case class Kid(id: String, name: PersonId)
Which approach is more idiomatic?
Are there any advantages of using value class in this case (no additional methods)?
Type aliases are purely a syntactic convenience—in some cases they can make code cleaner or easier to refactor, but they don't provide any additional type safety. For example, suppose I've got some code like this:
type DegreesC = Double
type DegreesF = Double
def c2f(c: DegreesC): DegreesF = (c * 9.0 / 5.0) + 32
And a value representing the current temperature in Fahrenheit:
val currentTempInF = 62.0
The compiler is happy to let me pass this to my c2f method:
scala> c2f(currentTempInF)
res1: DegreesF = 143.6
Value classes give you more type safety without the runtime cost of an additional allocation for the case class (although there is still a syntactic cost):
case class DegreesC(value: Double) extends AnyVal
case class DegreesF(value: Double) extends AnyVal
def c2f(c: DegreesC): DegreesF = DegreesF((c.value * 9.0 / 5.0) + 32)
val currentTempInF = DegreesF(62.0)
And then:
scala> c2f(currentTempInF)
<console>:14: error: type mismatch;
found : DegreesF
required: DegreesC
c2f(currentTempInF)
^
Which you prefer is a matter of taste. Personally I think type aliases in Scala are often overused and oversold, but I also tend to avoid value classes because they have weird limitations and bugs, and the runtime performance benefits they offer often aren't that important to me. In any case I wouldn't say one approach or the other is more idiomatic (if anything I'd give that status to a plain, non-value class case class).
For your specific use case (no additional methods). The big advantage is type safety. If you declare PersonId to be a type like this
type PersonId = String
your Kid case class will take any string as an argument for it's constructor.
but if you will declare PersonId with value type
case class PersonId(id: String) extends AnyVal
your Kid class will only accept a PersonId as an argument, this way you communicate the fact that this is not just any string but an actual PersonId.
The advantage of extending AnyVal over a regular case class is that it comes with no runtime overhead in most of the time.
You can checkout this link for more details.
Type aliases just provide an alias but no type safety.
type PersonId = String
case class Person(id: PersonId)
Person("some random string")
Compiles and runs without error
case class PersonId(id: String) extends AnyVal
case class Person(id: PersonId)
Person("some random string")
Fails at compile time with a type error

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

How best to keep a cached list of member fields, one each for a family of case classes in Scala

This is a follow up to the following question: Fastest way to get the names of the fields of a case class in Scala
I'm trying to find a simple way to provide fast custom serialization (lets say to a list of tuples of (String, Object), which can be converted into a db row in production or an in memory map in unit testing) to a family of case classes in Scala, and it seems that keeping a cached list of a fields of the class may be a promising way of doing this. However, I'm not sure about the cleanest way to do this. I know I can do something like the following:
case class Playlist(
val id: Option[Long],
val title: Option[String],
val album: Option[String],
val artist: Option[String],
val songId: Option[UUID]) {
def serialize = Playlist.fields.map(f => (f.getName, f.get(this)))
}
object Playlist {
val empty = Playlist(None, None, None, None, None)
val fields = Playlist.empty.getClass.getDeclaredFields.toList
fields foreach { _.setAccessible(true) }
}
There a are a couple of things I don't like about this, however:
I don't want to have to use empty from the companion class just to get a cached list of fields
I don't want to have to declare the serialization logic for each case class for which I want this serialization behavior. There are probably a few ways of getting around this, but I'm not sure of the cleanest way that will give correct behavior (worried about mixing reflection and inheritance)
What's the cleanest way to achieve this in Scala?
I think it would be simplest to keep a cache map of Class[_] -> fields separately from any individual case class, such as in a global singleton with a method serialize(instance). This way you don't have to write any extra code in the classes you wish to serialize.
Another way could be to create a trait to mixin to the case classes' companion objects, with the cached list of fields, and an implicit wrapper class to add the serialize method. You can use an implicit ClassTag to initialize fields:
abstract class MyCompanion[T](implicit ctag: ClassTag[T]) {
private val fields = ctag.runtimeClass.getDeclaredFields.toList
fields foreach { _.setAccessible(true) }
implicit class AddSerializeMethod(obj: T) {
def serialize = fields.map(f => (f.getName, f.get(obj)))
}
}
case class C(...) { ... }
object C extends MyCompanion[C]
Unfortunately, it seems you can't make AddSerializeMethod a value class this way.

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