there is Person model case class which is exposed to the client via REST endpoint.
scala> case class Person(firstname: String, lastname: String)
defined class Person
Now one of the internal service wants to "enrich" this Person case class and add social security number to it. BUT that enrichment is only to populate social security number from one service and pass it along to another one.
Immediate thought is to add ssn property to Person as follow - but that's not desired given Person is exposed to endpoint consumer and SSN MUST NOT be visible to any user calling REST endpoint which exposes Person data.
case class Person(firstname: String, lastname: String, ssn: String)
So another thought is to enrich Person case class as follow
scala> implicit class ExtendedPerson(person: Person){
| val SSN : String = ""
| }
defined class ExtendedPerson
then Service1.findPerson API will have implicit in the scope and will want to prepare Person as follow
val person = Person(firstName="Joe",lastname="Doe", ssn ="123-456-0000")
and then
Service2.moreWorkWithPerson(person,other arguments)
then Service2.moreWorkWithPerson will want to get ssn=123-456-0000 from person argument.
what's the right way to achieve this in Scala without modifying definition of Person case class? Can shapeless help? any pointers would be appreciated?
SSN as val in ExtendedPerson makes not much sense, instead it should be a function which queries your DB using the persons first and lastname. Given the fact that your DB is slow and you might call Person.SSN more than once I would suggest not to use this pattern as it will query your DB everytime you call .SSN. Instead I would write an implicit converter for Person to convert more explicitly.
case class ExtendedPerson(basicInfo: Person,ssn: String)
implicit class PersonHelper(person: Person) extends AnyVal{
def extended = {
val ssn = DB.querySSN(...)
ExtendedPerson(person,ssn)
}
}
val person: ExtendedPerson = Person("John","Doe").extended
//person = ExtendedPerson(Person("John","Doe"),"123-456-0000"")
Note: The code is written out of mind but should be logically correct
It seems that you are overthinking this. What's wrong with this?
case class Person(firstname: String, lastname: String, ssn: Option[String] = None);
Now, in REST handler you do return Person("John", "Doe"), and in the other service: return Person("John", "Doe", Some("111-11-1111"))
Maybe you should just expose a different class to the client.
case class Person(firstname: String, lastname: String, ssn: String) {
def toClient = ClientPerson(firstname, lastname)
}
case class ClientPerson(firstname: String, lastname: String)
object RESTService {
def getPerson(id: String): ClientPerson = {
val p: Person = DB.getPerson(id)
p.toClient
}
}
Related
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"))
We're using case classes to represent the JSON objects transferred between the client and server. It's been working great except for one sticking point we've been living with for quite a while now and I wonder if anyone has a clever way around it.
Let's say I have a user object that has id, first name, last name and email address. Once a user has been saved to the database, he has an id (Int) assigned to him, so for all communication between the client and server dealing with existing users, the id is a required field. In fact, there is only one case when the id field is not required and that's when the user is first being saved. The way we currently deal with this is with a case class that looks like this:
case class User(id: Option[Int], firstName: String, lastName: String, email:String)
In all cases except the initial save, that id is Some and for the initial save id is always None so we find ourselves using id.getOrElse(0) quite often. (Sometimes we'll do a .get but it feels dirty.)
What I would love to have is an object with an id: Int field for existing users and an object with no id field at all for new users, but without declaring all the other fields twice in two separate case classes. However, I'm not seeing a way to do that conveniently. I'm also not fond of using a 'magic' number for the id field of new users.
Does anyone have a better solution to this issue?
case class User[+IdOpt <: Option[Int]](idOpt: IdOpt, firstName: String, lastName: String, email:String)
object User {
// Type aliases for convenience and code readability
type New = User[None.type]
type Saved = User[Some[Int]]
type Value = User[Option[Int]] // New or Saved
implicit class SavedOps(val user: Saved) extends AnyVal {
def id: Int = user.idOpt.get
}
}
Tests:
scala> val billNew = User(None, "Bill", "Gate", "bill#microsoft.com")
billNew: User[None.type] = User(None,Bill,Gate,bill#microsoft.com)
scala> billNew.id
<console>:17: error: value id is not a member of User[None.type]
billNew.id
^
scala> val billSaved = billNew.copy(idOpt = Some(1))
billSaved: User[Some[Int]] = User(Some(1),Bill,Gate,bill#microsoft.com)
scala> billSaved.id
res1: Int = 1
This is what we ended up going with for now.
trait Resource[T <: Option[Int]] {
def idOpt: T
}
object Resource {
type IsSome = Some[Int]
implicit class SomeOps[R <: Resource[IsSome]](val resource: R) {
def id: Int = resource.idOpt.get
}
}
This allows us to use it like this:
case class User[T <: Option[Int]](idOpt:T, firstName:String, lastName:String, email:String) extends Resource[T]
case class Company[T <: Option[Int]](idOpt:T, companyName: String) extends Resource[T]
val u1 = User(None, "Bubba", "Blue", "bubba#shrimp.com")
val u2 = User(Some(1), "Forrest", "Gump", "forrest#shrimp.com")
u1.id // <-- won't compile
u2.id // <-- compiles
Having a magic number is not a terrible idea if you hide it from the user. in fact it is a common pattern, Slick uses it for example. You can just ignore the id value for the objects to be inserted.
So you can start by making the the constructor package private
case class User private[db](id: Int, firstName: String, lastName: String, email:String)
And then provide a companion object for users to create it without id
object User{
def apply(firstName: String, lastName: String, email: String): User = User(-1, firstName, lastName, email)
}
And now you can construct it as if id wasn't required
val user = User("first","last","email")
Suppose I have a case class like
case class Person(fname:String, lname:String, nickName:Option[String] = None)
On creating an instance like Person("John", "Doe"), I want nickName to be automatically assigned to fname, if one is not given. Eg:
val p1 = Person("John", "Doe")
p1.nickName.get == "John"
val p2 = Person("Jane", "Doe", "Joe")
p2.nickName.get == "Joe"
How can auto assignment of one field from another field be achieved?
Trying the solutions below in repl. I think this is something to do with repl
scala> case class Person(fname: String, lname:String, nickName:Option[String])
defined class Person
scala> object Person { def apply(fname:String, lname:String):Person = {Person(fname, lname, Some(fname))}}
console:9: error: too many arguments for method apply: (fname: String, lname: String)Person in object Person
object Person { def apply(fname:String, lname:String):Person = {Person(fname, lname, Some(fname))}}
You can overload the constructor of the case class
case class Foo(bar: Int, baz: Int) {
def this(bar: Int) = this(bar, 0)
}
new Foo(1, 2)
new Foo(1)
So you can check the case if nickName is none.
You can also overload it's apply method in the same way. In that way, then you can use
Foo(1,2)
Foo(1)
Technical Solution (don't)
On the current definition of case classes, you can override the constructor of the case class and the apply method of its companion object, as described in the answer of Facundo Fabre.
You will get something like this:
object Person {
def apply(fname:String, lname:String): Person = Person(fname, lname, fname)
}
case class Person(fname:String, lname:String, nickName: String) {
def this(fname:String, lname:String) = this(fname, lname, fname)
}
This is technical correct and quite clever coding. But for my taste its a little bit too clever, because it breaks an important property:
CaseClass.unapply(CaseClass.apply(x1,x2,x3)) == (x1,x2,x3)
In other words: When I construct a case class using apply with some tuple of parameter and then deconstruct it using unapply I expect to get the original tuple I put into apply (ignoring currying and option type).
But in this case, this property is not true any more:
Person.unapply(Person("John", "Smith"))
// result: Option[(String, String, String)] = Some((John,Smith,John))
Deconstruction using unapply is used for pattern matching (match{ case ... => ...). And this is a common use case for case classes.
So while the code is quite clever, it might confuse other people and break properties their code relies on.
Rephrase the problem (my suggestion)
When overly clever code is needed, it is often a good idea to rethink, what problem one tries to solve. In this case, I would suggest to distinguish between the nick name the user has chosen and a nick the system assigns to the user. In this case I would then just build a case class like this:
case class NickedPerson(fname : String, lname : String, chosenNick : Option[String] = None) {
val nick = chosenNick.getOrElse(fname)
}
You can then just use the field nick to access the computed nick name, or use chosenNick if you want to know if the user has provided that nick name:
NickedPerson("John", "Smith").nick
// result: String = "John"
NickedPerson("John", "Smith", Some("Agent")).nick
// result: String = "Agent"
The basic properties about case classes are not changed by this code.
Just explaining how to overload apply from companion object (in addition to #Facundo Fabre answer):
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Person {
def apply(fname:String, lname:String): Person = Person(fname, lname, fname)
}
case class Person(fname:String, lname:String, nickName: String)
// Exiting paste mode, now interpreting.
defined object Person
defined class Person
scala> Person("aaa", "bbb")
res24: Person = Person(aaa,bbb,aaa)
scala> Person("aaa", "bbb", "ccc")
res25: Person = Person(aaa,bbb,ccc)
You could also define default value using muti-parameter list constructor, but it's hard to use such case class (no toString and pattern matching for last parameter), so won't recommend (but it's good if you want simmilar thing for methods):
scala> case class Person(fname:String, lname:String)(val nickName: String = fname)
defined class Person
Another funny solution (just to play), which I wouldn't recommend to use in real code:
scala> case class Person(fname:String, lname:String, var nickName: String = null){nickName = Option(nickName).getOrElse(fname)}
defined class Person
scala> Person("aaa", "bbb")
res32: Person = Person(aaa,bbb,aaa)
I have this concrete class:
class Person (var name: String, var surname: String)
and I want to create another class that extends Person:
class Son (name: String, surname: String) extends Person(name, surname)
OK.
But I do want the fields in the constructor of Son must be var and not val.
How do I fix this? The fields must remain constructor parameters.
UPDATE #2
My problem is as follows:
If I define a method in Son, this does not work if I change the value to the parameters of an instance of Son.
class Son (name: String, surname: String) extends Person(name, surname){
def printSon = {
if(this.name=="name")println("Error name Person")
if(this.surname=="surname")println("Error surname Person")
}
}
object main {
def main(args: Array[String]): Unit = {}
val Marco = new Son("Marco","Bianchi")
Marco.printSon // So far everything is ok
Marco.name="name"
Marco.printSon // Here in control is not done, because Marco.name="name" writes in Person
println("FINE")
}
name e surname in Son are of type val.
If I understand correctly you want the name and surname fields in Son to be immutable, as opposed to those in Person, which are vars.
That simply isn't possible. The only way to do it would be by overwriting:
class Person (var name: String, var surname: String)
//this won't compile
class Son(override val name: String, override val surname: String) extends Person(name, surname)
In Scala you cannot overwrite a var with a val - and it is pretty obvious why - subclasses can only override or add functionality to a superclass. If we wore to allow vals to override vars we would remove from the parent class the setter for the overridden field. This breaks the "is-a" relationship implied by inheritance.
What you could do is implement this like so:
trait Person {
def name: String
def surname: String
}
//this is fine now
class Son(val name: String, val surname: String) extends Person
//in case you want a type of person which can be changed.
class MutablePerson(var name: String, var surname: String) extends Person
Person now is a trait - it just provides a way of getting the name or surname. Son overrides Person by using vals - and thus you get the guarantee that nobody will change the fields in Son ever again.
From your example I assume that you still want to have a type of Person that can be modified. And you can do that using the MutablePerson class above, which does allow it's fields to be mutated.
Hope this helps !
You have to name the params different, because otherwise the methods in the body of son will try to access the params and not the fields in the Person class. Scala will automatically create fields for the params if you access them in the body. It will work, when you do something like this (which also is a commonly used pattern):
class Son (_name: String, _surname: String) extends Person(_name, _surname){
def printSon = {
if(name == "name")println("Error name Person")
if(surname == "surname")println("Error surname Person")
}
}
I'm fairly new to Scala and I have a question about the best way to copy a case class while preserving data that comes from traits. For example, let's say I have the following:
trait Auditing {
var createTime: Timestamp = new Timestamp(System.currentTimeMillis)
}
case class User(val userName: String, val email: String) extends Auditing
val user = User("Joe", "joe#blah.com")
Then I want to make a new copy with one parameter changed:
val user2 = user.copy(email = "joe#newemail.com")
Now, in the example above, the property createTime does not get copied over because it is not defined in the constructor of the User case class. So my question is: assuming that moving createTime into the constructor is not an option, what is the best way for getting a copy of the User object that includes the value from the trait?
I'm using Scala 2.9.1
Thanks in advance!
Joe
You can override the copy method with that behavior.
case class User(val userName: String, val email: String) extends Auditing
{
def copy(userName = this.userName, email = this.email) {
val copiedUser = User(userName, email)
copiedUser.createTime = createTime
copiedUser
}
}
While I see no other solution than Reuben's, I don't understand the requirement to leave the constructor args untouched. This would be the most natural solution:
case class User(userName: String, email: String,
override val createTime:Timestamp = new Timestamp(System.currentTimeMillis))
extends Auditing
If you don't want the user to be able to overwrite createTime, you can still use:
case class User private (userName: String, email: String,
override val createTime:Timestamp) extends Auditing {
def this(userName: String, email: String) =
this(userName, email, new Timestamp(System.currentTimeMillis))
}
The only drawback is that you need to write new User("Joe", "joe#blah.com"), as the primary constructor is now private.
You might be better of not using a case class. You can easily implement the
functionality you need yourself. The below code implements the copy method you wanted, a constructor without new, hides the original constructor, and creates an extractor so that you can use User in case statements.
class User private(val userName: String,
val email: String,
val timeStamp: Timestamp =
new Timestamp(System.currentTimeMillis)) {
def copy(uName: String = userName,
eMail: String = email) =
new User(uName, eMail, timeStamp)
}
object User {
def apply(userName: String, email: String) =
new User(userName, email)
def unapply(u: User) = Some((u.userName, u.email, u.timeStamp))
}