Working with options in Scala (best practices) - scala

I have a method that I wrote to enrich person data by performing an API call and adding the enriched data.
I have this case class:
case class Person(personData: PersonData, dataEnrichment: Option[DataEnrichment])
My method is supposed to return this case class, but I have few filters before, in case person height is not "1.8 m" OR if personId was not found in the bio using the regex, I want to return Person with dataEnrichment = None . My issue is that person height and personId are Options themselves, so it looks like this:
def enrichPersonObjWithApiCall(person: Person) = {
person.personData.height.map(_.equals("1.8 m")) match {
case Some(true) =>
val personId = person.personData.bio flatMap { comment =>
extractPersonIdIfExists(comment)
}
personId match {
case Some(perId) =>
apiCall(perId) map { apiRes =>
Person(
person.personData,
dataEnrichment = apiRes)
}
case _ =>
Future successful Person(
person.personData,
dataEnrichment = None)
}
case _ =>
Future successful Person(
person.personData,
dataEnrichment = None)
}
}
def extractPersonIdIfExists(personBio: String): Option[String] = {
val personIdRegex: Regex = """(?<=PersonId:)[^;]+""".r
personIdRegex.findFirstIn(personBio)
}
def apiCall(personId: String): Future[Option[DataEnrichment]] = {
???
}
case class DataEnrichment(res: Option[String])
case class PersonData(name: String, height: Option[String], bio: Option[String])
It doesn't seem to be a Scala best practice to perform it like that. Do you have a more elegant way to get to the same result?

Using for is a good way to process a chain of Option values:
def enrichPersonObjWithApiCall(person: Person): Future[Person] =
(
for {
height <- person.personData.height if height == "1.8 m"
comment <- person.personData.bio
perId <- extractPersonIdIfExists(comment)
} yield {
apiCall(perId).map(Person(person.personData, _))
}
).getOrElse(Future.successful(Person(person.personData, None)))
This is equivalent to a chain of map, flatMap and filter calls, but much easier to read.

Here, I tried to make it more idiomatic and shorter:
def enrichPersonObjWithApiCall(person: Person) = {
person.personData.height.collect {
case h if h == "1.8 m" =>
val personId = person.personData.bio.flatMap(extractPersonIdIfExists)
personId.map(
apiCall(_)
.map(apiRes => person.copy(dataEnrichment = apiRes))
)
}.flatten.getOrElse(
Future.successful(person.copy(dataEnrichment = None))
)
}
Basically, the idea is to use appropriate monadic chains of map, flatMap, collect instead of pattern matching when appropriate.

Same idea as Aivean's answer. Just I would use map flatMap and filter.
def enrichPersonObjWithApiCall(person: Person) = {
person.personData.height
.filter(_ == "1.8 m")
.flatMap{_=>
val personId = person.personData.bio
.flatMap(extractPersonIdIfExists)
personId.map(
apiCall(_)
.map(apiRes => person.copy(dataEnrichment = apiRes))
)
}.getOrElse(Future.successful(person))
}
It's more readable for me.

Related

Use higher order functions to concise scala code

I'm new to Scala and trying to write some programs to get better at it. I wrote a flow (version 1) that is very Java-like and I'm trying to write it using higher order functions (version 2).
version 1:
val entry: Option[Int] = getEntry()
if (entry.isDefined) {
val cachedEntry = entry.get
if (cachedEntry.state.isActive) {
return cachedEntry
} else {
Cache.invalidateCachedEntry(cachedEntry)
}
}
Cache.createNewEntry()
version 2:
val entry: Option[Int] = getEntry()
entry.filter(_.state.isActive).orElse((() => {
Cache.invalidateCachedEntry _
Option(Cache.createNewEntry())
})()).get
I'm not sure if this is the correct approach or there is a better way of doing this?
Let's consider following scenerio:
case class Entry(state: AnyState)
case class AnyState(isActive: Boolean = true)
object Cache {
def invalidateCachedEntry(entry: Entry): Unit = println("cleaned")
}
def getEntry: Option[Entry] = Some(Entry(AnyState()))
val optEntry: Option[Entry] = getEntry
val result: Option[Entry] = optEntry match {
case Some(entry) if entry.state.isActive =>
entry // do something
println("did something")
Some(entry)
case Some(entry) =>
Cache.invalidateCachedEntry(entry)
None
case _ =>
println("Entry not found")
None
}
This would be a one scenario. In general you should return something. But sometimes you don't have enough information. Such cases you can return Option and if you want to throw an error you can use Either
I prefer using match for clarity:
getEntry() match {
case Some(entry) if entry.state.isActive => entry
case opt => opt.foreach(Cache.invalidateCachedEntry); Cache.createNewEntry()
}

DSL in scala using case classes

My use case has case classes something like
case class Address(name:String,pincode:String){
override def toString =name +"=" +pincode
}
case class Department(name:String){
override def toString =name
}
case class emp(address:Address,department:Department)
I want to create a DSL like below.Can anyone share the links about how to create a DSL and any suggestions to achieve the below.
emp.withAddress("abc","12222").withDepartment("HR")
Update:
Actual use case class may have more fields close to 20. I want to avoid redudancy of code
I created a DSL using reflection so that we don't need to add every field to it.
Disclamer: This DSL is extremely weakly typed and I did it just for fun. I don't really think this is a good approach in Scala.
scala> create an Employee where "homeAddress" is Address("a", "b") and "department" is Department("c") and that_s it
res0: Employee = Employee(a=b,null,c)
scala> create an Employee where "workAddress" is Address("w", "x") and "homeAddress" is Address("y", "z") and that_s it
res1: Employee = Employee(y=z,w=x,null)
scala> create a Customer where "address" is Address("a", "b") and "age" is 900 and that_s it
res0: Customer = Customer(a=b,900)
The last example is the equivalent of writing:
create.a(Customer).where("address").is(Address("a", "b")).and("age").is(900).and(that_s).it
A way of writing DSLs in Scala and avoid parentheses and the dot is by following this pattern:
object.method(parameter).method(parameter)...
Here is the source:
// DSL
object create {
def an(t: Employee.type) = new ModelDSL(Employee(null, null, null))
def a(t: Customer.type) = new ModelDSL(Customer(null, 0))
}
object that_s
class ModelDSL[T](model: T) {
def where(field: String): ValueDSL[ModelDSL2[T], Any] = new ValueDSL(value => {
val f = model.getClass.getDeclaredField(field)
f.setAccessible(true)
f.set(model, value)
new ModelDSL2[T](model)
})
def and(t: that_s.type) = new { def it = model }
}
class ModelDSL2[T](model: T) {
def and(field: String) = new ModelDSL(model).where(field)
def and(t: that_s.type) = new { def it = model }
}
class ValueDSL[T, V](callback: V => T) {
def is(value: V): T = callback(value)
}
// Models
case class Employee(homeAddress: Address, workAddress: Address, department: Department)
case class Customer(address: Address, age: Int)
case class Address(name: String, pincode: String) {
override def toString = name + "=" + pincode
}
case class Department(name: String) {
override def toString = name
}
I really don't think you need the builder pattern in Scala. Just give your case class reasonable defaults and use the copy method.
i.e.:
employee.copy(address = Address("abc","12222"),
department = Department("HR"))
You could also use an immutable builder:
case class EmployeeBuilder(address:Address = Address("", ""),department:Department = Department("")) {
def build = emp(address, department)
def withAddress(address: Address) = copy(address = address)
def withDepartment(department: Department) = copy(department = department)
}
object EmployeeBuilder {
def withAddress(address: Address) = EmployeeBuilder().copy(address = address)
def withDepartment(department: Department) = EmployeeBuilder().copy(department = department)
}
You could do
object emp {
def builder = new Builder(None, None)
case class Builder(address: Option[Address], department: Option[Department]) {
def withDepartment(name:String) = {
val dept = Department(name)
this.copy(department = Some(dept))
}
def withAddress(name:String, pincode:String) = {
val addr = Address(name, pincode)
this.copy(address = Some(addr))
}
def build = (address, department) match {
case (Some(a), Some(d)) => new emp(a, d)
case (None, _) => throw new IllegalStateException("Address not provided")
case _ => throw new IllegalStateException("Department not provided")
}
}
}
and use it as emp.builder.withAddress("abc","12222").withDepartment("HR").build().
You don't need optional fields, copy, or the builder pattern (exactly), if you are willing to have the build always take the arguments in a particular order:
case class emp(address:Address,department:Department, id: Long)
object emp {
def withAddress(name: String, pincode: String): WithDepartment =
new WithDepartment(Address(name, pincode))
final class WithDepartment(private val address: Address)
extends AnyVal {
def withDepartment(name: String): WithId =
new WithId(address, Department(name))
}
final class WithId(address: Address, department: Department) {
def withId(id: Long): emp = emp(address, department, id)
}
}
emp.withAddress("abc","12222").withDepartment("HR").withId(1)
The idea here is that each emp parameter gets its own class which provides a method to get you to the next class, until the final one gives you an emp object. It's like currying but at the type level. As you can see I've added an extra parameter just as an example of how to extend the pattern past the first two parameters.
The nice thing about this approach is that, even if you're part-way through the build, the type you have so far will guide you to the next step. So if you have a WithDepartment so far, you know that the next argument you need to supply is a department name.
If you want to avoid modifying the origin classes you can use implicit class, e.g.
implicit class EmpExtensions(emp: emp) {
def withAddress(name: String, pincode: String) {
//code omitted
}
// code omitted
}
then import EmpExtensions wherever you need these methods

scala returns doesn't conform to required S_

I got the error
found : scala.concurrent.Future[Option[models.ProcessTemplatesModel]]
required: Option[models.ProcessTemplatesModel]
My function is below
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(_.id === processTemplateId).result.map(_.headOption)
val result: Future[Option[ProcessTemplatesModel]] = db.run(action)
result.map { case (result) =>
result match {
case Some(r) => {
var copy = (processTemplates returning processTemplates.map(_.id)) += ProcessTemplatesModel(None, "[Copy of] " + r.title, r.version, r.createdat, r.updatedat, r.deadline, r.status, r.comment, Some(false), r.checkedat, Some(false), r.approvedat, false, r.approveprocess, r.trainingsprocess)
val composedAction = copy.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
db.run(composedAction)
}
}
}
}
what is my problem in this case?
edit:
my controller function looks like this:
def createCopyOfProcessTemplate(processTemplateId: Int) = Action.async {
processTemplateDTO.createCopyOfProcessTemplate(processTemplateId).map { process =>
Ok(Json.toJson(process))
}
}
Is there my failure?
According to the your code - there are the following issues:
You use two db.run which return futures, but inner future will
not complete. For resolving it you should compose futures with
flatMap or for-comprehension.
You use only one partial-function case Some(_) => for pattern matching
and don't handle another value None.
You can use only one db.run and actions composition.
Your code can be like as:
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(...).result.map(_.headOption)
val composedAction = action.flatMap {
case Some(r) =>
val copyAction = (processTemplates returning processTemplates...)
copyAction.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
case _ =>
DBIO.successful(None) // issue #2 has been resolved here
}
db.run(composedAction) // issue #3 has been resolved here
}
We get rid of issue #1 (because we use actions composition).

Dynamic orderBy with Squeryl

I can not figure out how to change orderBy dynamically in runtime. I need something like:
def samplesSorted(fields: List[String]) = {
from(Schema.samples)(s => select(s) orderBy(fields.map(getterByName))
}
Or something like
def samplesSorted(fields: List[String]) = {
val q = from(Schema.samples)(s => select(s))
fields.forEach(field => q.addOrderBy(getterByName(field)))
q
}
I am trying to write a help function to manipulate AST now. But that does not seem like the right solution.
Did not notice there is a version of orderBy that accepts a list of ExpressionNodes. Was able to solve it like this:
def samplesSorted(fields: List[String]) = {
from(Schema.samples)(s => select(s) orderBy(fields.map(buildOrderBy(s)))
}
def buildOrderBy(row: Row)(field: String): ExpressionNode = {
getterByName(row, field)
}
def getterByName(row: Row, field: String): String = field match {
case "Name" => row.name
case "Address" => row.address
}
Have not tried with fields of different types yet - implicits may not work in this case. But I could always call them explicitly.
Upd:
To do the same with descending order one could use a helper like this one:
def desc(node: ExpressionNode):ExpressionNode = new OrderByArg(node) {desc}
This works for me
def ord(dr: DataRow, name: String): ExpressionNode = if (orderAscending) {
dr.getterByName(name) asc
} else {
dr.getterByName(name) desc
}
case class DataRow(id: Long,
#Column("resource_id") resourceId: String,
def getterByName(name: String) = {
name match {
case "resource_id" => resourceId.~
case _ => id.~
}
}
}
from(DataSchema.dataRows) { dr =>
where(dr.id === id).select(dr).orderBy(ord(dr, filedName))
}.page(offset, limit)

Is there a best practice to assign a value to a scala Option when it's not initialized/None?

I have the following class where the properties are an Option[T]
class User extends IdBaseEntity[UUID] {
var id: Option[UUID] = None
var name: Option[String] = None
var createdOn: Option[Date] = None
}
In some data access layer I need to assign these properties if they aren't set before the object is persisted to cassandra. Here are a few ways for the createdOn property. Are any of these the best approach or is there something better I should be doing?
Example 1
entity.createdOn = Some(entity.createdOn.map(identity).getOrElse(new Date()))
Example 2
entity.createdOn = entity.createdOn.orElse(Some(new Date()))
Example 3
entity.createdOn = entity.createdOn match {
case None => Some(new Date())
case _ => entity.createdOn
}
Example 4
entity.createdOn = entity.createdOn match {
case None => Some(new Date())
case Some(x) => Some(x)
}
Example 5
entity.createdOn match {
case None => entity.createdOn = Some(new Date())
case _ =>;
}
Matching on Option is not really idiomatic (IMHO). I prefer to get orElse or getOrElse. Personally I would go with example 2.
I'm not sure whether this will fit your use case, but it is more idiomatic to make User an immutable case class:
case class User(id: Option[UUID] = None, ...)
and copy it, rather than updating the fields in-place:
val updatedEntity = entity.copy(name = user.name.orElse(Some("Chris")))
I'd consider changing your design a bit - for two reasons:
It looks like the User class should be read-only once initialized, so something like a case class or val instead of var would capture that requirement:
case class User( id:UUID, name:String, createdOn:Date );
It looks like every User is required to have an id, name, and createdOn property set, so Option[] is not a good way to model that.
I often setup a Builder class along side read-only classes to
simplify and decouple the object-construction process from
what the object represents - something like
this
object User {
class Builder {
var id:UUID = UUID.randomUUID()
def id( v:UUID ):this.type = {id =v; this; }
var name:String = id.toString
def name( v:String ):this.type = { name=v; this; }
var createdOn:Date = new Date()
def createdOn( v:Date ):this.type = { createdOn = v; this; }
def build():User = {
assert( Seq(id,name,createdOn).find( _ == null ).isEmpty, "Must set all props" )
User( user, name, createdOn )
}
}
}
Anyway - that's another way to do things ...
Since the scenario is "get a property value and update it if some condition holds", I'd try to encapsulate access to properties. For example:
/**
* Read+write access to property `P` of object `R`.
*/
case class Accessor[R,P](get: R => P, set: (R, P) => Unit) {
/** Utility for updating values. */
def update(record: R, modfn: P => P) =
set(record, modfn(get(record)));
}
class User {
var id: Option[Int] = None;
}
object User {
// For each property of `User` we need to define an accessor,
// but this is one time job:
val idA: Accessor[User,Option[Int]] =
Accessor((u: User) => u.id,
(u: User, r: Option[Int]) => u.id = r);
}
object Test {
import User._;
// We can do a lot of stuff now with accessors, for example set
// default values for `Option[...]` ones:
def setDefault[A,B](record: A,
accessor: Accessor[A,Option[B]],
defPropVal: => B) =
accessor.update(record, _.orElse(Some(defPropVal)));
val user = new User();
// Set user's id, if not defined:
setDefault(user, idA, 42);
}
So instead of defining a specific method for each property for filling in default values, we define a generic accessor for each property. Then we can use them to implement all the other stuff generically.