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)
Related
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
I have these 2 classes. In reality, both have a lot of fields. Somewhere, I need only field of superclass, taken from Person class.
There is a nice way to get only super class values and not mapping them field by field?
*I'm pretty sure I'll have some problems with json writes for class Details (which is not a case class and have not a singleton object, but this is another subject)
If I get your question correctly, then you might be asking me runtime polymorphism or dynamic method dispatch from java.
If so, you may have to create both the class and not case class
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
Now create the object of person and reference to superclass (Details)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
This way you will be able to get the only address and age
Another way is like , why can't we create the Details a separate entity like:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
Output:
println(detail.details)
Details(10,Europe)
I will post a solution that leverages scala macro system (old kind, not the newest introduced with Scala 3.0). It could be an overkill for you...
BTW, if you want to access to only parent values (for example for getting key, value pair), you can:
given a type tag, get all parents;
from them, extract all the accessors (vals);
for each val, get its value;
and finally returns a list with all accessors taken
So, I try to solve each point step by step.
First of all, we have to write the macro definition as:
object Macros {
def accessors[T](element : T): String = macro MacrosImpl.accessors[T]
}
object MacrosImpl {
def accessors[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[String] = ...
}
for the first point, we can leverage the reflection macroprogramming API using c.universe:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
for the second point, we can iterate over the parent classes and then take only the public accessors:
val accessors = parents
.map(weakType.tpe.baseType(_))
.flatMap(_.members)
.filter(_.isPublic)
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.toSet
So, for example, if the we write Macros.accessors[Details](person), accessors will yield age and address.
To take the value, we can leverage quasiqouting. So, first we take only the values name:
val names = accessors
.map(_.fullName)
.map(_.split("\\."))
.map(_.reverse.head)
Then we convert them into a TermName:
val terms = names.map(TermName(_))
And finally, we convert each term to a key value tuple containing the val name and its value:
val accessorValues = terms
.map(name => c.Expr[(String, Any)](q"(${name.toString}, ${element}.${name})"))
.toSeq
The last step consist in convert a Seq[Expr[(String, Any)] into a Expr[Seq[(String, Any)]. A way to do that, could be leveraging recursion, reify, and splicing expression:
def seqToExprs(seq: Seq[Expr[(String, Any)]]): c.Expr[Seq[(String, Any)]] =
seq.headOption match {
case Some(head) =>
c.universe.reify(
Seq((head.splice._1, head.splice._2)) ++
seqToExprs(seq.tail).splice
)
case _ => c.Expr[Seq[(String, Any)]](q"Seq.empty")
}
So now I decide to return a String representation (but you can manipulate it as you want):
val elements = seqToExprs(accessorValues)
c.Expr[String](q"${elements}.mkString")
You can use it as:
import Macros._
class A(val a : Int)
class B(val b : Int) extends A(b)
class C(val c: Int) extends B(c)
//println(typeToString[List[Set[List[Double]]]])
val c = new C(10)
println(accessors[C](c)) // prints (a, 10)(b, 10)(c, 10)
println(accessors[B](c)) // prints (a, 10)(b, 10)
println(accessors[A](c)) // prints (a, 10)
And, using your example:
// Your example:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
println(accessors[Details](person)) // prints (address,Europe)(age,33)
println(accessors[Person](person)) // prints (address,Europe)(age,33)(name,Alex)
Here there is a repository with the macro implemented.
Scala 3.0 introduce a safer and cleaner macro system, if you use it and you want to go further you can read these articles:
macros tips and tricks
short tutorial
another tutorial
For example, how is this:
class Cat(name: String, val age: Int) {
def this() = this("Garfield", 20)
}
val someCat = new Cat
someCat.age
res0: Int = 20
Different from:
class Cat(name: String = "Garfield", val age: Int = 20)
val someCat = new Cat
someCat.age
res0: Int = 20
Note:
I have seen answers to other questions(e.g here) that discuss the differences between Java & Scala in the implementation for auxiliary constructors. But I am mostly trying to understand why do we need them in Scala, in the first place.
Auxiliary constructors are good for more than just supplying defaults. For example, here's one that can take arguments of different types:
class MyBigInt(x: Int) {
def this(s: String) = this(s.toInt)
}
You can also hide the main constructor if it contains implementation details:
class MyBigInt private(private val data: List[Byte]) {
def this(n: Int) = this(...)
}
This allows you to have data clearly be the backing structure for your class while avoiding cluttering your class with the arguments to one of your auxiliary constructors.
Another use for auxiliary constructors could be migrating Java code to Scala (or refactoring to change a backing type, as in the example above) without breaking dependencies. In general though, it is often better to use a custom apply method in the companion object, as they are more flexible.
A recurring use case I noticed is, as Brian McCutchon already mentioned in his answer "For example, here's one that can take arguments of different types", parameters of Option type in the primary constructor. For example:
class Person(val firstName:String, val middleName:Option[String], val lastName: String)
To create a new instance you need to do:
val person = new Person("Guido", Some("van"), "Rossum")
But with an auxiliary constructor, the whole process will be very pleasant.
class Person(val firstName:String, val middleName:Option[String], val lastName: String){
def this(firstName:String, middleName:String, lastName: String) = this(firstName, Some(middleName), lastName)
}
val person = new Person("Guido", "van", "Rossum")
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"))
What is the FP idiomatic way for this: let's say I have this
trait Name
object Name{
def apply(name: String): Name = {
if (name.trim.isEmpty || name.trim.length < 3)
InvalidName
else
ValidName(name.trim)
}
}
case object InvalidName extends Name
case class ValidName(name:String) extends AnyVal with Name
Now I have some helper functions such as
def split = name.splitAt(" ")
//some more functions
which way is more idiomatic:
Put them in the case class it self but that some how makes the case class to contain some logic however I can do something like :
val n = ValidName("john smith")
val (first, last) = n.split
Put them in dedicated object but then the method will look like
def split(n: ValidName) = n.name.splitAt(" ")
Create an object with implicit class that will accept Name and will call the methods
What do you think ?
There is no major issue with adding logic to a case class, especially when it is just extracting the data in a different format. (It becomes problematic when you add data members to a case class).
So I would do option 1 and not worry about it!
In response to the comments, case class is actually just a shortcut for creating a class with a whole bunch of useful pre-implemented methods. In particular, the unapply method allows a case class to be used in pattern matching, and equals does an element-wise comparison of the fields of two instances.
And there are a bunch of other methods to extract the data from the case class in different ways. The most obvious are toString and copy, but there are others like hashCode and all the stuff inherited from Product, such as productIterator.
Since a case class already has methods to extract the data in useful ways, I see no objection to adding your split method as another way of extracting data from the case class.
More idiomatic:
case class Name private (name: String) {
lazy val first :: last :: Nil = name.split(" ").toList
}
object Name {
def fromString (name: String): Either[String, Name] = {
if (name.trim.isEmpty || name.trim.length < 3) Left("Invalid name")
else Right(new Name(name.trim))
}
}
Or maybe this:
case class Name (first: String, last: String) {
lazy val fullName = s"$first $last"
}
object Name {
def fromString (name: String): Either[String, Name] = {
if (name.trim.isEmpty || name.trim.length < 3) Left("Invalid name")
else {
val first :: last :: Nil = name.split(" ").toList
Right(new Name(first, last))
}
}
}
In scala it's more idiomatic to represent failure cases through the use of Either than through inheritance. If you have an instance of N, you can't call any functions on it, you'll probably have to pattern match it. But a type like Either already comes with functions like map, fold, etc. that makes it easier to work with.
Having a private constructor helps ensure that you can only create a valid Name because the only way to create one is through the fromString method.
DO NOT use implicits for this. There's no need and would only make for confusing code. Not really what implicits are for.
I think it depends on context. In this case, if most of the methods you are using are just slight tweaks to String methods, you might want to consider a fourth option:
case class Name(name: String)
implicit def NameToString(n: Name) = n.name
Name("Iron Man").split(" ") // Array(Iron, Man)
I'd like to read a string as an instance of a case class. For example, if the function were named "read" it would let me do the following:
case class Person(name: String, age: Int)
val personString: String = "Person(Bob,42)"
val person: Person = read(personString)
This is the same behavior as the read typeclass in Haskell.
dflemstr answered more towards setting up the actual read method- I'll answer more for the actual parsing method.
My approach has two objects that can be used in scala's pattern matching blocks. AsInt lets you match against strings that represent Ints, and PersonString is the actual implementation for Person deserialization.
object AsInt {
def unapply(s: String) = try{ Some(s.toInt) } catch {
case e: NumberFormatException => None
}
}
val PersonRegex = "Person\\((.*),(\\d+)\\)".r
object PersonString {
def unapply(str: String): Option[Person] = str match {
case PersonRegex(name, AsInt(age)) => Some(Person(name, age))
case _ => None
}
}
The magic is in the unapply method, which scala has syntax sugar for. So using the PersonString object, you could do
val person = PersonString.unapply("Person(Bob,42)")
// person will be Some(Person("Bob", 42))
or you could use a pattern matching block to do stuff with the person:
"Person(Bob,42)" match {
case PersonString(person) => println(person.name + " " + person.age)
case _ => println("Didn't get a person")
}
Scala does not have type classes, and in this case, you cannot even simulate the type class with a trait that is inherited from, because traits only express methods on an object, meaning that they have to be "owned" by a class, so you cannot put the definition of a "constructor that takes a string as the only argument" (which is what "read" might be called in OOP languages) in a trait.
Instead, you have to simulate type classes yourself. This is done like so (equivalent Haskell code in comments):
// class Read a where read :: String -> a
trait Read[A] { def read(s: String): A }
// instance Read Person where read = ... parser for Person ...
implicit object ReadPerson extends Read[Person] {
def read(s: String): Person = ... parser for Person ...
}
Then, when you have a method that depends on the type class, you have to specify it as an implicit context:
// readList :: Read a => [String] -> [a]
// readList ss = map read ss
def readList[A: Read] (ss: List[String]): List[A] = {
val r = implicitly[Read[A]] // Get the class instance of Read for type A
ss.map(r.read _)
}
The user would probably like a polymorphic method like this for ease of use:
object read {
def apply[A: Read](s: String): A = implicitly[Read[A]].read(s)
}
Then one can just write:
val person: Person = read[Person]("Person(Bob,42)")
I am not aware of any standard implementation(s) for this type class, in particular.
Also, a disclaimer: I don't have a Scala compiler and haven't used the language for years, so I can't guarantee that this code compiles.
Starting Scala 2.13, it's possible to pattern match a Strings by unapplying a string interpolator:
// case class Person(name: String, age: Int)
"Person(Bob,42)" match { case s"Person($name,$age)" => Person(name, age.toInt) }
// Person("Bob", 42)
Note that you can also use regexes within the extractor.
Which in this case, helps for instance to match on "Person(Bob, 42)" (age with a leading space) and to force age to be an integer:
val Age = "[ ?*](\\d+)".r
"Person(Bob, 42)" match {
case s"Person($name,${Age(age)})" => Some(Person(name, age.toInt))
case _ => None
}
// Person = Some(Person(Bob,42))
The answers on this question are somewhat outdated. Scala has picked up some new features, notably typeclasses and macros, to make this more easily possible.
Using the Scala Pickling library, you can serialize/deserialize arbitrary classes to and from various serialization formats:
import scala.pickling._
import json._
case class Person(name: String, age: Int)
val person1 = Person("Bob", 42)
val str = person1.pickle.value // { tpe: "Person", name: "Bob", age: 42 }
val person2 = JSONPickle(str).unpickle[Person]
assert(person1 == person2) // Works!
The serializers/deserializers are automatically generated at compile time, so no reflection! If you need to parse case classes using a specific format (such as the case class toString format), you can extend this system with your own formats.
The uPickle library offers a solution for this problem.
Scala uses Java's serialization stuff, with no String representation.