Where to put complex logic of Scala case class default value - scala

I am new to Scala and i want to understand, where to put the complex logic for default values of Case Classes.
case class Job (name: String, timeStamp: Long = <something more complex>) {
...
}
Where should i put the more complex logic? (For example not just assigning a value)
Do I need to overwrite the apply method, or create a companion object?

Simply add an additional apply method to the companion object:
case class Job(name: String, timeStamp: Long)
object Job {
def apply(name: String): Job = new Job(name, System.currentTimeMillis)
}
val j1 = Job("foo", 345678L)
val j2 = Job("bar")
Now, inside the apply, you have the freedom to make arbitrarily complex computations that can depend on name too, without requiring multiple argument lists.

Related

case class with logic what is the idiomatic way

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)

Scala collection whose elements can construct sibling instances using named parameters and default values?

I want to have a collection of objects, each object a companion of a different class, which classes all share a common method defined in a superclass that can be invoked when looping through the collection with a foreach(). I want the constructors of these sibling-classes to have the same named parameters and default parameter values as each other. Finally, I want to minimize repeated code.
Thus far, I am trying to do this with case classes, since--if it worked--it would eliminate all the duplicated code of the companion-objects for each type. The problem is that if I put all these companion objects into a Set, when I take them out again I lose the default parameters and parameter names.
Here is some example code of what I am describing:
trait MyType {
val param: String
def label = param // all instances of all subclasses have this method
}
case class caseOne(override val param: String = "default") extends MyType
case class caseTwo(override val param: String = "default") extends MyType
object Main extends App {
// I can construct instances using the companion objects' `apply()` method:
val works1 = caseOne(param = "I have been explicitly set").label
// I can construct instances that have the default parameter value
val works2 = caseOne().label
// But what I want to do is something like this:
val set = Set(caseOne, caseTwo)
for {
companion <- set
} {
val fail1 = companion() // Fails to compile--not enough arguments
val fail2 = companion(param = "not default") // Fails also as param has lost its name
val succeeds = companion("nameless param") // this works but not what I want
println(fail1.label + fail2.label) // this line is my goal
}
}
Notably if the Set has only one element, then it compiles, suggesting the inferred type of the multi-element Set lacks the parameter name--even though they are the same--and the default values. Also suggesting that if I gave the Set the right type parameter this could work. But what would that type be? Not MyType since that is the type of the companion classes rather that the objects in the Set.
I could define the companion objects explicitly, but that is the repeated code I want to avoid.
How can I loop through my collection, constructing instances of MyType subclasses on each iteration, with constructors that have my desired parameter names and default values? All while minimizing repeated code?
Update: Originally the example code showed caseOne and caseTwo as having different default values for param. That was incorrect; they are now the same.
You're not going to be able to get exactly what you want since you don't really have much control over the auto-generated companion objects. In particular for this to work they would all need to extend a common trait. This is why it fails to compile when the set has more than one companion object; even though they all have a method with the same signature, they don't extend a common trait for the compiler to utilize.
You can use a nested case class and get something very similar though:
trait MyType {
val param: String
def label = param // all instances of all subclasses have this method
}
abstract class MyTypeHelper(default: String) {
case class Case(param: String) extends MyType
def apply(param: String) : Case = Case(param)
def apply(): Case = apply(default)
}
object One extends MyTypeHelper("default one")
object Two extends MyTypeHelper("default two")
object Example {
val works1 = One(param = "I have been explicitly set").label
val works2 = One().label
val set = Set(One, Two)
for {
companion <- set
} {
val a = companion()
val b = companion(param = "not default")
val c = companion("nameless param")
println(a.label + b.label)
}
}
Instead of having a caseOne type, you have One.Case, but it still implements MyType so you shouldn't have any issue anywhere else in the code that uses that trait.

Scala: why can't I declare values in a constructor?

I would like to declare some auxiliary values inside a case class constructor, but it seems not to be correct Scala.
In short, the following piece of code is correct:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
this(
s"date: ${datetime.toLocalDate.toString()}",
s"time: ${datetime.toLocalTime.toString()}"
)
}
}
and the following is not:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
val date = datetime.toLocalDate.toString()
val time = datetime.toLocalTime.toString()
this(
s"date: $date",
s"time: $time"
)
}
}
even though the latter would be more legible and easier to maintain. (Imagine using more complex operations than just calling two methods.) Why is that?
Is there another way to write a constructor like that or a way to work around this?
In Scala first call must be to primary constructor. After that you can have as much code as you want. Read this for explanation.
Similar rule applies to Java for this and super. Not exactly same though. Read this.
The reason why this and super must be first is, that one can set fields to various values before that actual this(x, y) is called. This means object is being constructed and different values can be visible to any thread that may have reference to the object while construction is in progress.
Thanks.
In your second case you are not allowed to define variables inside constructor before this(params) call, as computing inside constructors are discouraged in scala class or case class. One way you can fix it is pass inline constructor params.
test("test case class custom constructor") {
case class Something(text1: String,text2: String) {
def this(datetime: LocalDateTime) {
this(datetime.toLocalDate.toString(), datetime.toLocalTime.toString())
//you can do whatever you want after this(x, y) is invoked
val testVal = "apple"
println(testVal)
}
}
new Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
new Something(LocalDateTime.now()).text2 should not be empty
}
Another way (Encouraged way) is define case class and then define apply inside a companion object as below (for older version maybe 2.11.8, companion object had to be defined first and only case class which seems to be fixed now - https://issues.scala-lang.org/browse/SI-3772)
test("test class with companion apply method") {
case class Something(val text1: String, val text2: String) {}
object Something {
def apply(datetime: LocalDateTime): Something = {
val x = datetime.toLocalDate.toString()
val y = datetime.toLocalTime.toString()
new Something(x, y)
}
}
Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
Something(LocalDateTime.now()).text2 should not be empty
}
scastie code - https://scastie.scala-lang.org/prayagupd/yn2bJWHkQ6Gbli5Ll6I6CQ/1
Auxiliary constructors have a constraint that it should call a previous auxiliary constructor or primary constructor on the first line of its body. The second code does not follow that rule. Hence the error.

transform anorm (play framework) value before binding

I have a case class representing my domain:
case class MyModel(rawValue: String, transformedValue: String)
rawValue maps to a value in the database and is properly parsed and bound. What I am trying to do is add transformedValue to my model: this value is just some arbitrary transformation that I perform on the rawValue. It does not map to any data in the database / query.
I have a parser (prior to adding transformedValue) that looks like this:
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue)
}
}
Since MyModel is immutible and I can't insert transformedValue into it after its been created, what and where is the best way to do and add this transformation (e.g. adding ad-hoc values to the Model) preferably without using vars?
Coming from Java, I would probably just have added a getTransformedValue getter to the domain class that performs this transformation on the rawValue attribute.
Since transformedValue seems to be a derived property, and not something that would be supplied in the constructor, you can define it as an attribute in the body of the function, possibly using the lazy qualifier so that it will only be computed on-demand (and just once for instance):
case class MyModel(rawValue: String) {
lazy val transformedValue: String = {
// Transformation logic, e.g.
rawValue.toLowerCase()
}
}
val is evaluated when defined; def is evaluated when called. A lazy val is evaluated when it is accessed the first time.
It may be appropriate to use lazy in the case of an expensive computation that is rarely needed, or when logic requires it. But for most cases a regular val should be used. Quoting:
lazy val is not free (or even cheap). Use it only if you absolutely need laziness for correctness, not for optimization.
I don't see why you wouldn't just do the transformation in the parser itself:
def transformation(rawValue: String): String = ...
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue, transformation(rawValue))
}
}
Or if you don't want to do it there for some reason, you can use copy to create a new copy of MyModel with a new value:
val model = MyModel("value", ..)
val modelWithTransform = model.copy(transformedValue = transformation(model.rawValue))
You could also overload apply to automatically apply the transformation within the companion object:
case class MyModel(rawValue: String, transformedValue: String)
object MyModel {
def apply(value: String): MyModel = MyModel(rawValue, transformation(rawValue))
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue)
}
}
}
MyModel may be immutable, but you can always create another copy of it with some values changed.
Turns out it was easier than I thought and the solution did look like what I said I would have done in java:
I just add a function to MyModel that does this transformation:
case class MyModel(rawValue: String) {
def transformedValue = {
// do the transformation here and return it
}
}

How to combine class cases?

I am using play framework and slick, play framework uses a case map in the form validation but there are values that I don't need validated as they are not inputed by the user, e.g. the ID & Date which is done is provided on the backend.
By the end I would like to have a class case like this, to provide to Slick and use with my Table.
case class Order(id: Long, order: String, date: Date)
For Play's Form validation I would provide a seperate case class:
case Class inputableOrder(order: String)
Can I then create the Order class that will grab the variables from inputableOrder and added it the Order class?
case class Order(id: Long, date: Date) // <- some way to add the variable from inputableOrder?
I'm just trying to prevent repetition but I still need two different case classes (one for form validation and another for working with the database).
Is there a way to modify an existing case Class , remove a variable or modify a variable type?
I think you have several options here:
Make InputableOrder a part of Order:
case class InputableOrder(order: String) // ...
case class Order(input: InputableOrder, id: Long, date: Date) // ..
This is probably the most idiomatic solution. But it can be inflexible if you later realize that InputableOrder needs something that shouldn't be in Order.
Make Order a subclass of InputableOrder. In this case there is some code repetition when passing arguments to the superclass, and the superclass can't be a case class, so you have to declare it as a regular class and create an extractor yourself:
class InputableOrder(val order: String) // ...
object InputableOrder {
def unapply(o: InputableOrder): Option[String] = Some(o.order);
// if you have more than one constructor arguments, return
// a tuple like Option[(String,String)] etc.
}
case class Order(override val order: String, id: Long, date: Date)
extends InputableOrder(order) // ...
Again, the same problems can arise as with the previous point.
Make the classes distinct and create helper methods to convert between them. The choice depends on your design, but I find this solution to be most flexible:
case class InputableOrder(order: String);
case class Order(order: String, id: Long, date: java.util.Date) {
// An additional constructor to use when converting from Inputable:
def this(other: InputableOrder, id: Long, date: java.util.Date) =
this(other.order, id, date);
// Update this instance with `other`:
def this(that: Order, other: InputableOrder) =
this(other, that.id, that.date);
def toInput = InputableOrder(order);
}
This way you can create an Order from an InputableOrder just by supplying the missing fields and vice versa. You need to write these helper methods/constructors once, but using them is then easy.
You can also use implicit methods such as
implicit def toInput(other: InputableOrder): Order = other.toInput;
to make things even easier.