When to use object and when to use class in Scala - scala

I have a service object called MyService with functions defined that are used by my Play application's controllers. One particular function in MyService is parsing some text, and turning it into a JSON object. So my process will be:
Parse some text containing unstructured book info (title, author etc) into some Scala objects (Book objects)
Convert the Book objects into JSON format
Return the JSON
What I am wondering is, in the step where I parse the text and create my Scala objects, how should I define them? If this was Java I would just have an inner class named 'Book', but with Scala I don't know whether I should define an inner object or inner class inside my MyService object, and I don't know why/when I would choose one over the other.
Could someone explain when to use an object and when to use a class?

You use object when you only want EXACTLY ONE instance of your class.
objects can't have parameters and it's methods and values can be accessed via MyObject.myMethod.
In Java you would use the singleton pattern to achieve what object is in Scala.
Then you would have something like MyObject.getInstance().myMethod.
In your case you want to parse information into a class. I would make the parser class an object (assuming the parsing process is static).
The result however is not static, since it relies on the parsed data. Book should definitly be a class. (If there is exactly one Book, the parsing would not make much sense, would it?)

#Kigyo's answer is fine, but there's something else that should be addressed here. You're working within an MVC framework, and this Book you're describing sounds exactly like a model. This MyService object you describe is starting to sound like it's very bloated, and serializing a Book as JSON is something you can do entirely within a Play model.
package models
import play.api.libs.json._
case class Book(title: String, author: String, pages: Int)
object Book {
/** Use one of Play's json macros to define an implicit serializer/deserializer */
implicit val jsonFormat: Format[Book] = Json.format[Book]
def parse(rawText: String): Book = {
// Move your book parsing logic to here and construct the new Book instance.
}
}
Then in your controller function you would do something like:
import play.api.libs.json._
val rawText: String = ... // The raw text you're parsing the book data from.
val book: Book = Book.parse(rawText) // The parsed Book
val json: JsValue = Json.toJson(book) // The Book as a Play JSON object (can be returned as a JSON response)
val jsonString: String = Json.stringify(json) // The Book JSON as a String object, in case you need that..
This is a little more verbose than it needs to be, but I separated it out line-by-line to illustrate what's happening at each step. If all you wanted was the JSON as a string Jsons.stringify(Json.toJson(Book.parse(rawText))) would suffice.
Now the Book logic is self-contained, and not cluttering up another object.

Related

ReactiveMongo Macros.handler crashes after new field is added

I recently add a new field to my scala case class since I want to start keeping track of that field in my MongoDB.
let's say case class is like this:
case class MyItem (
var existingField: String,
var newlyAddedField: String
) {
}
I use this to serialize/deserialize json and bson to my object:
object MyItem {
import play.api.libs.json.Json
// generate reader and writer:
implicit var jsonFormats = Json.format[MyItem]
implicit var bsonFormats = Macros.handler[MyItem]
}
As all the existing data in my db doesn't have newlyAddedField, I get runtime exception
reactivemongo.bson.exceptions.DocumentKeyNotFound: The key 'newlyAddedField' could not be found in this document or array
Could anyone help? I've read about writing my own serialization/deserialization but I am not sure how to do that as I am using Play Framework whose syntax and way to do things are all different among its versions. I hope there is a simpler way as adding field should be common in NoSQL db. Btw, I am using play framework 2.5
Thanks in advance!
AFAIU it Macros.handler is null-safe i.e. it doesn't sets value to null if there is no field. I think the simplest and the cleanest fix this in Scala is to declare your new field as Option[String] so everyone (including the macro code-generator) will see that this field might be absent. And this seems to be what the doc suggests as well

Scala immutability in persistent storage with Squeryl

So as I am reading the Play for Scala book, I came across something odd that was explained in the book. This is the relevant snippet:
There's something strange going on, though. If you're using immutable
classes—which vanilla case classes are—you might be worried when you
discover that Squeryl updates your object's supposedly immutable id
field when you insert the object. That means that if you execute the
following code,
val myImmutableObject = Product(0, 5010255079763,
"plastic coated blue", "standard paperclip, coated with blue plastic")
Database.productsTable.insert(myImmutableObject)
println(myImmutableObject)
the output will unexpectedly be something like: Product(13,
5010255079763, "plastic coated blue", "standard paperclip, coated with
blue plastic"). This can lead to bad situations if the rest of your
code expects an instance of one of your model classes to never change.
In order to protect yourself from this sort of stuff, we recommend you
change the insert methods we showed you earlier into this:
def insert(product: Product): Product = inTransaction {
val defensiveCopy = product.copy
productsTable.insert(defensiveCopy)
}
My question is, given that the product class is defined like this:
import org.squeryl.KeyedEntity
case class Product(
id: Long,
ean: Long,
name: String,
description: String) extends KeyedEntity[Long]
Database object is defined like this:
import org.squeryl.Schema
import org.squeryl.PrimitiveTypeMode._
object Database extends Schema {
val productsTable = table[Product]("products")
...
on(productsTable) { p => declare {
p.id is(autoIncremented)
}}
}
How then is it possible that a case class declared as val can have one of its fields changed? Is Squeryl using reflection of some sort to change the field or is the book mistaken somehow?
I am not able to run the examples to verify what the case might be, but someone who has used Squeryl can perhaps give an answer?
You can check the definition of table method for yourself:
https://github.com/squeryl/squeryl/blob/master/src/main/scala/org/squeryl/Schema.scala#L345
It's a generic function which does use reflection to instantiate the Table object bound to the given case class. Functions are first-class citizens in Scala, so they can be assigned to a val just like anything else.
The last fragment is also an asynchronous function, which maps a given argument to some modification defined for it.

Copying almost identical objects in Scala (aka. How to package models for REST)?

I have several objects that closely (but not perfectly) mirror other objects in Scala. For example, I have a PackagedPerson that has all of the same fields as the PersonModel object, plus some. (The PackagedPerson adds in several fields from other entities, things that are not on the PersonModel object).
Generally, the PackagedPerson is used for transmitting a "package" of person-related things over REST, or receiving changes back (again over REST).
When preparing these transactions, I have a pack method, such as:
def pack(p: PersonModel): PackagedPerson
After all the preamble is out of the way (for instance, loading optional, extra objects that will be included in the package), I create a PackagedPerson from the PersonModel and "everything else:"
new PackagedPerson(p.id, p.name, // these (and more) from the model object
x.profilePicture, y.etc // these from elsewhere
)
In many cases, the model object has quite a few fields. My question is, how can I minimize repetitive code.
In a way it's like unapply and apply except that there are "extra" parameters, so what I really want is something like this:
new PackagePerson(p.unapply(), x.profilePicture, y.etc)
But obviously that won't work. Any ideas? What other approaches have you taken for this? I very much want to keep my REST-compatible "transport objects" separate from the model objects. Sometimes this "packaging" is not necessary, but sometimes there is too much delta between what goes over the wire, and what gets stored in the database. Trying to use a single object for both gets messy fast.
You could use LabelledGeneric from shapeless.
You can convert between a case class and its a generic representation.
case class Person(id: Int, name: String)
case class PackagedPerson(id: Int, name: String, age: Int)
def packagePerson(person: Person, age: Int) : PackagedPerson = {
val personGen = LabelledGeneric[Person]
val packPersonGen = LabelledGeneric[PackagedPerson]
// turn a Person into a generic representation
val rec = personGen.to(person)
// add the age field to the record
// and turn the updated record into a PackagedPerson
packPersonGen.from(rec + ('age ->> age))
}
Probably the order of the fields of your two case classes won't correspond as nice as my simple example. If this is the case shapeless can reorder your fields using Align. Look at this brilliant answer on another question.
You can try Java/Scala reflection. Create a method that accepts a person model, all other models and model-free parameters:
def pack(p: PersonModel, others: Seq[Model], freeParams: (String, Any)*): PackedPerson
In the method, you reflectively obtain PackedPerson's constructor, see what arguments go there. Then you (reflectively) iterate over the fields of PersonModel, other models and free args: if there's a field the name and type of which are same as one of the cunstructor params, you save it. Then you invoke the PackedPerson constructor reflectively using saved args.
Keep in mind though, that a case class can contain only up to 22 constructor params.

What is the reasoning behind the Book.scala file contents in simple-rest-scala

In the activator template for simple rest API project in Scala the Book.scala file looks like the following.
package models
import play.api.libs.json.Json
object Book {
case class Book(name: String, author: String)
implicit val bookWrites = Json.writes[Book]
implicit val bookReads = Json.reads[Book]
var books = List(Book("TAOCP", "Knuth"), Book("SICP", "Sussman, Abelson"))
def addBook(b: Book) = books = books ::: List(b)
}
Why is there a Book object and a Book case class inside it? Why not just a Book case class (or just a Book class and not a case class)? What advantages/disadvantages are there in the above structure?
I'm sure this is just a small example that somebody put together, and so you shouldn't read too much into it. But it exhibits what some consider an anti-pattern: nesting case classes in other classes. Some best-practices guides, such as this one, suggest avoiding nesting case classes in other classes, and for good reason:
It is tempting, but you should almost never define nested case classes
inside another object/class because it messes with Java's
serialization. The reason is that when you serialize a case class it
closes over the "this" pointer and serializes the whole object, which
if you are putting in your App object means for every instance of a
case class you serialize the whole world.
And the thing with case classes specifically is that:
one expects a case class to be immutable (a value, a fact) and hence
one expects a case class to be easily serializable
Prefer flat hierarchies.
For example, this small program throws an exception, somewhat unexpectedly:
import java.io._
class Outer {
case class Inner(a: Int)
}
object Test extends App {
val inner = (new Outer).Inner(1)
val oos = new ObjectOutputStream(new FileOutputStream("/tmp/test"))
oos.writeObject(inner)
oos.close
}
If the only purpose of this outer Book object is to group together common functionality, a package would be the preferred structure.
Furthermore, even if an object were desired for some other reason, naming that object the same as the inner case class is confusing, especially since case classes automatically generate companion objects. So in this example there is a Book object, a Book.Book case class, and therefore also a Book.Book companion object.
The role of Book object in this code is more like a static book utils/manager class which hold a list of books. You can imagine that this is a Library class, which allow to add books.
The Book case class is just an anonymous class for Book instances. As m-z said, it is just an example, for more complicated class, you could move it to a standalone Book class.

Scala v 2.10: How to get a new instance of a class (object) starting from the class name

I have tens of JSON fragments to parse, and for each one I need to get an instance of the right parser. My idea was to create a config file where to write the name of the class to instantiate for each parser (a kind of map url -> parser) . Getting back to your solution, I cannot call the method I implemented in each parser if I have a pointer to Any. I suppose this is a very common problem with a well-set solution, but I have no idea what the best practices could be.
I really have no experience with Java, Reflection, Class Loading and all that stuff. So,
can anyone write for me the body of the method below? I need to get an instance of a class passed as String (no arguments needed for the constructor, at least so far...)
def createInstance(clazzName: String) = {
// get the Class for the given clazzName, e.g. "net.my.BeautifulClazz"
// instantiate an object and return it
}
Thanks, as usual...
There is a very simple answer:
scala> def createInstance(clazzName: String) = Class.forName(clazzName).newInstance
createInstance: (clazzName: String)Any
scala> createInstance("java.lang.String")
res0: Any = ""
If it works for you, everything is fine. If it don't, we have to look into your class loader. This is usually the point when things will get dirty.
Depending in what you want to do, look into:
The cake pattern, if you want to combine your classes during compile time
OSGi when you want to build a plugin infrastructure (look here for a very simple example)
Google guice, if you really need dependency injection (e.g. when mixing Scala and Java code) and the cake pattern does not work for you