Reputation: 8043
The title of this question is a tad weird but I want to accomplish is as follows.
I have a List
of Tasks
. For the sake of convenience the task is defined as follows:
case class Task(name: String)
I have a TaskStorage
trait with a store method that will persist the task and return a Try[Task]
.
My storage doesn't have a batch storage API so I need to mimic batch storage in the application end. The way I was initially doing was as follows:
val tasks = List(task1, task2)
tasks.map(taskStorage) -> This returns a List[Try[Task]]
My API design might be a little suspect here but what I want is some thing as follows:
def batchStoreTasks(tasks: List[Task]):Try[Task] = {
//
}
The Try[Task]
indicates the status of the last task that was asked to be persisted by the storage. Since I was unable to find an idiomatic way to accomplish the above, I resorted to pattern match and did the following:
def batchStoreTasks(tasks: List[Task]): Try[Task] = tasks match {
case Nil => Failure(EmptyTaskListException)
case x :: Nil => taskStorage(x)
case x :: t => val storedTask = taskStorage(x); if(storedTask.isSuccess) batchStoreTasks(t) else storedTask
}
I am able to get the job done but I am missing the idiomatic way to accomplish the above. It will be great if I could be pointed in the right direction to restructure the batchStoreTasks to give it a more idiomatic shape.
Thanks
Upvotes: 1
Views: 89
Reputation: 688
Belw a possible solution, I am using view on the list to make sure the map is lazy and taskStorage is not evaluated after unessary, in the example only task "A" and "B" will be printed:
case class Task(name: String)
class TaskStorage {
def apply(task: Task): Try[Task] = {
if (task.name == "B"){
println("Failed")
Failure(new RuntimeException)
}
else {
println("Success")
Success(task)
}
}
}
val taskStorage: TaskStorage = new TaskStorage()
val tasks = List(Task("A"), Task("B"), Task("C"))
tasks.view.map(taskStorage(_)).collectFirst {
case r if r.isFailure => r
}.getOrElse(taskStorage(tasks.head))
In RxJava/RxScala, this could be easily implemented:
case class Task(name: String)
trait TaskStorage {
def apply(task: Task): Try[Task]
}
val taskStorage: TaskStorage = _ //Instance of concrete task storage
def batchStoreTasks(tasks: List[Task]): Observable[Task] = {
for {
taskName <- Observable.from(tasks)
taskResult <- Observable.from(taskStorage(taskName))
}
yield taskResult
}
Upvotes: 1
Reputation: 641
You can use takeWhile
def batchStoreTasks(tasks: List[Task]): Try[Task] =
if(tasks.isEmpty) Failure(EmptyTaskListException) else {
val tryStore = tasks.diff(tasks.takeWhile(x => taskStorage(x).isSuccess))
tryStore.map(_ => Failure(new RuntimeException(tryStore.head.toString))).headOption.getOrElse(Success(tryStore.reverse.head))
}
Upvotes: 0
Reputation: 44918
Assuming that you want to return the failure of the last task if the list was non-empty but all tasks failed:
val tryTasks = tasks.map(taskStorage).reverse
tryTasks
.find(_.isSuccess) // try to find a success
.orElse(tryTasks.headOption) // return last failure
.getOrElse(Failure(EmptyTaskListException)) // special failure for empty list
Note that it will usually throw away the failure information captured in the failed tasks, but that's what I understood from your description.
Upvotes: 2