How to reduce boilerplate with Kleisli - scalaz

I follow the design of the book Functional and Reactive Domain Modeling
And for some service methods, it only delegates work to the repository layer. Is there a way to reduce this boilerplate :
trait FeedbackServiceImpl extends FeedbackService {
override def saveTFE(feedback: TripFeedbackEvent) =
Kleisli[Future, Context, Either[String, Id]] { ctx => ctx.feedbackRepo.save(feedback) }
override def saveLFE(feedback: LibraryFeedbackEvent) =
Kleisli[Future, Context, Either[String, Id]] { ctx => ctx.feedbackRepo.save(feedback) }
override def findByUser(userId: Id) =
Kleisli[Future, Context, Seq[FeedbackEvent]] { ctx => ctx.feedbackRepo.findByUser(userId) }
override def all =
Kleisli[Future, Context, Seq[FeedbackEvent]] { ctx => ctx.feedbackRepo.all }
override def findByTip(tipId: Id) =
Kleisli[Future, Context, Seq[FeedbackEvent]] { ctx => ctx.feedbackRepo.findByTip(tipId) }
}

We can create a combinator :
private def kleisli[M[_], A](f: FeedbackRepository => M[A]) =
Kleisli.kleisli(f).local[Context](_.feedbackRepo)
Hence we gain 2 things :
avoid declaring the type by helping the type inference mechanism
avoid calling ctx.feedbackRepo by using local
So we can use :
trait Feedbacks {
def saveTFE(feedback: TripFeedbackEvent) = kleisli(_.save(feedback))
def saveLFE(feedback: LibraryFeedbackEvent) = kleisli(_.save(feedback))
def findByUser(userId: Id) = kleisli(_.findByUser(userId))
...
}

Can you just define a function that does all the boilerplate ? Something like:
def repo2Kleisli[T](f: Repo => Future[T]): Kleisli[Future, Context, T]
You could possibly even make it implicit and reduce your code to something like:
override def saveTFE(feedback: TripFeedbackEvent) = (repo: Repo) => repo.save(feedback)

Related

Insert record into Db using Slick (Scala), Best practices for an Entity

First to say, I'm newcomer in Scala and really need a little help. I need to build a web api, and I'll try to insert one record into database, but have some problems with mapping the entity (db table) into a model (class). I worked with .Net Core Web API (there I used Entity Framework Core, here in Scala use Slick) and try to keep same arhitecture in Scala, but need some more informations, because on the internet I find a lot of versions, and can not choose the best.
As database, MySQL is used.
User.scala
case class User(
id: Int = 0,
userName: String,
firstName: String,
lastName: String
) {
override def equals(that: Any): Boolean = true
}
object User {
implicit object UserFormat extends Format[User] {
def writes(user: User): JsValue = {
val userSeq = Seq(
"id" -> JsNumber(user.id),
"userName" -> JsString(user.userName),
"firstName" -> JsString(user.firstName),
"lastName" -> JsString(user.lastName)
)
JsObject(userSeq)
}
def reads(json: JsValue): JsResult[User] = {
JsSuccess(User(
(json \ "id").as[Int].value,
(json \ "userName").as[String].value,
(json \ "firstName").as[String].value,
(json \ "lastName").as[String].value)
)
}
}
def tupled = (this.apply _).tupled
}
class UserMap #Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit ex: ExecutionContext) {
val dbConfig: DatabaseConfig[JdbcProfile] = dbConfigProvider.get[JdbcProfile]
val db: JdbcBackend#DatabaseDef = dbConfig.db
val dbUsers = TableQuery[UserDef]
def getAll(): Unit = {
val action = sql"SELECT Id, UserName, FirstName, LastName FROM Users".as[(Int, String, String, String)]
return db.run(action)
}
def add(user: User): Future[Seq[User]] = {
dbUsers += user
db.run(dbUsers.result)
}
}
UserDef.scala (which is a mapper of db table / entity)
class UserDef(tag: Tag) extends Table[User](tag, "Users") {
def id = column[Int]("Id", O.PrimaryKey, O.AutoInc)
def userName = column[String]("UserName")
def firstName = column[String]("FirstName")
def lastName = column[String]("LastName")
override def * = (id, userName, firstName, lastName) <> (create, extract)
def create(user: (Int, String, String, String)): User = User(user._1, user._2, user._3, user._4)
def extract(user: User): Option[(Int, String, String, String)] = Some((user.id, user.userName,user.firstName,user.lastName))
}
UsersController.scala
def createUser = Action(parse.json) { implicit request => {
val userJson = request.body
var user = new User(
-1,
(userJson \ "userName").as[String].value,
(userJson \ "firstName").as[String].value,
(userJson \ "lastName").as[String].value
)
var users = TableQuery[UserDef]
Await.result(db.run(DBIO.seq(
users += user,
users.result.map(println))), Duration.Inf
)
Ok(Json.toJson(user))
}
}
How I see the problem:
UserDef is an Entity and must remain clean, only table columns definitions
UserMap is the bridge between User class and UserDef (entity), can be used as a repository with crud methods (getAll(), getById(id), create(user), update(user), delete(id)). This is in same file as User class, but probably must be moved in another.
User class is the model and need to contain only their parameters and writes/reads (Scala specifics)
and now in the controller:
If I try to insert a record into database, with current method, first I need to get all rows from table, and then to add the new record in the list. What happening if I have 3 4mil records in this table? Will get all these rows useless to insert only a new row.
Then, after inserting this new row, I need to return it into client, but how I can get it updated (Id is every time -1, but if I get entire list to see what it contain, I can see the correct id for the newest entity)
thx
Finally, I found a good solution and post it here, maybe somebody need this:
UserMap, for me at least will become UserRepository. There I have CRUD operations and maybe some extra :
def getAll(): Future[Seq[User]] = {
db.run(dbUsers.result)
}
def getById(id: Int): Future[Option[User]] ={
val action = dbUsers.filter(_.id === id).result.headOption
db.run(action)
}
def create(user: User): Future[User] = {
val insertQuery = dbUsers returning dbUsers.map(_.id) into ((x, id) => x.copy(id = id))
val action = insertQuery += user
db.run(action)
}
def update(user: User) {
Try( dbUsers.filter(_.id === user.id).update(user)) match {
case Success(response) => db.run(response)
case Failure(_) => println("An error occurred!")
}
}
def delete(id: Int) {
Try( dbUsers.filter(_.id === id).delete) match {
case Success(response) => db.run(response)
case Failure(_) => println("An error occurred!")
}
}
and UsersController:
def getAll() = Action {
var users = Await.result(usersRepository.getAll(), Duration.Inf)
Ok(Json.toJson(users))
}
def getById(id: Int) = Action { implicit request => {
val user = Await.result(usersRepository.getById(id), Duration.Inf)
Ok(Json.toJson(user))
}
}
def create = Action(parse.json) { implicit request => {
val userJson = request.body
var user = new User(
-1,
(userJson \ "userName").as[String].value,
(userJson \ "firstName").as[String].value,
(userJson \ "lastName").as[String].value
)
var createdUser = Await.result(usersRepository.create((user)), Duration.Inf)
Ok(Json.toJson(createdUser))
}
}
def update(id: Int) = Action(parse.json) { implicit request => {
val userJson = request.body
var user = new User(
(userJson \ "id").as[Int].value,
(userJson \ "userName").as[String].value,
(userJson \ "firstName").as[String].value,
(userJson \ "lastName").as[String].value
)
var updatedUser = usersRepository.update(user)
Ok(Json.toJson(user))
}
}
def delete(id: Int) = Action {
usersRepository.delete(id)
Ok("true")
}
Anyway, I know I have some bad blocks of code there...especially in create & update methods, where convert json to User.
I wanted to give it a try, and here is a full working example of a Play 2.7/Scala 2.13/Slick 4.0.2 REST-API controller bound to a MySQL database.
Since you are starting with Scala, maybe it is a bit overwhelming at first to get eased with Play, Slick, etc...
So here is an humble skeleton (derived from Play-Slick GitHub)
So first, since we want to write an API, here is the conf/routes file:
GET /users controllers.UserController.list()
GET /users/:uuid controllers.UserController.get(uuid: String)
POST /users controllers.UserController.create()
PUT /users controllers.UserController.update()
DELETE /users/:uuid controllers.UserController.delete(uuid: String)
Nothing to fancy here, we just bind routes to functions in the upcoming controller.
Just notice that the 2nd GET and the DELETE expect an UUID as query param, while Json bodies with be used for the POST and PUT.
It would be nice to see the model right now, in app/models/User.scala:
package models
import java.util.UUID
import play.api.libs.json.{Json, OFormat}
case class User(
uuid: UUID,
username: String,
firstName: String,
lastName: String
) {
}
object User {
// this is because defining a companion object shadows the case class function tupled
// see: https://stackoverflow.com/questions/22367092/using-tupled-method-when-companion-object-is-in-class
def tupled = (User.apply _).tupled
// provides implicit json mapping
implicit val format: OFormat[User] = Json.format[User]
}
I used an uuid instead using a numerical id, but basically, it is the same.
Notice that a Json serializer/deserializer can be written in just one line (you don't need to detail it with case classes). I think it is also a good practice to not override it to produce Seq as found on your code, since this serializer will be very usefull when converting objects to Json on the controller.
Now the tupled definition is most likelly a hack (see comment) that will be required later on the DAO...
Next, we need a controller in app/controllers/UserController.scala:
package controllers
import java.util.UUID
import forms.UserForm
import javax.inject.Inject
import play.api.Logger
import play.api.data.Form
import play.api.i18n.I18nSupport
import play.api.libs.json.Json
import play.api.mvc._
import services.UserService
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
class UserController #Inject()(userService: UserService)
(implicit ec: ExecutionContext) extends InjectedController with I18nSupport {
lazy val logger: Logger = Logger(getClass)
def create: Action[AnyContent] = Action.async { implicit request =>
withFormErrorHandling(UserForm.create, "create failed") { user =>
userService
.create(user)
.map(user => Created(Json.toJson(user)))
}
}
def update: Action[AnyContent] = Action.async { implicit request =>
withFormErrorHandling(UserForm.create, "update failed") { user =>
userService
.update(user)
.map(user => Ok(Json.toJson(user)))
}
}
def list: Action[AnyContent] = Action.async { implicit request =>
userService
.getAll()
.map(users => Ok(Json.toJson(users)))
}
def get(uuid: String): Action[AnyContent] = Action.async { implicit request =>
Try(UUID.fromString(uuid)) match {
case Success(uuid) =>
userService
.get(uuid)
.map(maybeUser => Ok(Json.toJson(maybeUser)))
case Failure(_) => Future.successful(BadRequest(""))
}
}
def delete(uuid: String): Action[AnyContent] = Action.async {
Try(UUID.fromString(uuid)) match {
case Success(uuid) =>
userService
.delete(uuid)
.map(_ => Ok(""))
case Failure(_) => Future.successful(BadRequest(""))
}
}
private def withFormErrorHandling[A](form: Form[A], onFailureMessage: String)
(block: A => Future[Result])
(implicit request: Request[AnyContent]): Future[Result] = {
form.bindFromRequest.fold(
errors => {
Future.successful(BadRequest(errors.errorsAsJson))
}, {
model =>
Try(block(model)) match {
case Failure(e) => {
logger.error(onFailureMessage, e)
Future.successful(InternalServerError)
}
case Success(eventualResult) => eventualResult.recover {
case e =>
logger.error(onFailureMessage, e)
InternalServerError
}
}
})
}
}
So here:
basically, each of our 5 functions referenced from the routes file check input, and then delegate the work to an injected UserService (more on that later)
for the create and update functions, you can see that we use Play Forms that I think is also a good practice. Their role is to validate the incoming Json, and that Marshall it into a User type.
Also, you can see that we use Action.async: Scala offers a very powerfull leverage with Futures so lets use it! Basically by doing so, you ensure that your code is not-blocking, thus easing the IOPS on your hardware.
Finally for the case of GET (one), GET (all), POST and PUT, since we return users, and have a deseralizer, a simple Json.toJson(user) do the work.
Before jumping to service and dao, lets see the form, in app/forms/UserForm.scala:
package forms
import java.util.UUID
import models.User
import play.api.data.Form
import play.api.data.Forms.{mapping, nonEmptyText, _}
object UserForm {
def create: Form[User] = Form(
mapping(
"uuid" -> default(uuid, UUID.randomUUID()),
"username" -> nonEmptyText,
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
)(User.apply)(User.unapply)
)
}
Nothing too fancy here, just as the doc says, although there is just a trick : when no uuid is defined (in the POST case, then we generate one).
Now, the service... not so much required in this very case, but in practice it might be a good thing to have an extra layer (dealing with acls for example), in app/services/UserService.scala:
package services
import java.util.UUID
import dao.UserDAO
import javax.inject.Inject
import models.User
import scala.concurrent.{ExecutionContext, Future}
class UserService #Inject()(dao: UserDAO)(implicit ex: ExecutionContext) {
def get(uuid: UUID): Future[Option[User]] = {
dao.get(uuid)
}
def getAll(): Future[Seq[User]] = {
dao.all()
}
def create(user: User): Future[User] = {
dao.insert(user)
}
def update(user: User): Future[User] = {
dao.update(user)
}
def delete(uuid: UUID): Future[Unit] = {
dao.delete(uuid)
}
}
As you can see, here, it is just a wrapper around the dao, and finnally the dao in app/dao/UserDao.scala:
package dao
import java.util.UUID
import javax.inject.Inject
import models.User
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import play.db.NamedDatabase
import slick.jdbc.JdbcProfile
import scala.concurrent.{ExecutionContext, Future}
class UserDAO #Inject()(#NamedDatabase("mydb") protected val dbConfigProvider: DatabaseConfigProvider)(implicit executionContext: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
private val users = TableQuery[UserTable]
def all(): Future[Seq[User]] = db.run(users.result)
def get(uuid: UUID): Future[Option[User]] = {
db.run(users.filter(_.uuid === uuid).result.headOption)
}
def insert(user: User): Future[User] = {
db.run(users += user).map(_ => user)
}
def update(user: User): Future[User] = {
db.run(users.filter(_.uuid === user.uuid).update(user)).map(_ => user)
}
def delete(uuid: UUID): Future[Unit] = {
db.run(users.filter(_.uuid === uuid).delete).map(_ => ())
}
private class UserTable(tag: Tag) extends Table[User](tag, "users") {
def uuid = column[UUID]("uuid", O.PrimaryKey)
def username = column[String]("username")
def firstName = column[String]("firstName")
def lastName = column[String]("lastName")
def * = (uuid, username, firstName, lastName) <> (User.tupled, User.unapply)
}
}
So, here I have just adapted the code from the official play-slick example, so I guess, I do not have better comment than theirs...
Hope, the whole things helps to get a better picture :)
If something is unclear, feel free to ask!

Play 2.4 ActionBuilder / ActionFunction, BodyParsers and JSON

In a Play 2.4 project, I created an Action that do three things.
check that a specific header is present. If not, returns a HTTP error.
use the header value to authenticate the user (a authentication function will be passed to that Action). If the auth fails returns an HTTP error
parse the JSON body to a case class and give it to the Action block code.
To do that, I used the Play Action composition mecanism explain in this page : https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition
but more specifically, the final result I wanted is explained here :
https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together
I succeeded to write this:
package actions
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json._
import play.api.mvc.Results._
import play.api.mvc.{WrappedRequest, _}
import scala.concurrent.Future
object Actions {
case class WithApiKeyRequest[A](apiKey: String, request: Request[A]) extends WrappedRequest[A](request)
case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) extends WrappedRequest[A](request)
def AuthenticatedAndParsed[T, A](authencation: String => Future[_])(implicit reader: Reads[T]): ActionBuilder[ParsedJsonRequest] =
WithApiKeyHeaderAction andThen AuthentificationAction(authencation) andThen JsonAction
private[this] def WithApiKeyHeaderAction = new ActionBuilder[WithApiKeyRequest] {
override def invokeBlock[A](request: Request[A], block: (WithApiKeyRequest[A]) => Future[Result]): Future[Result] =
request.headers.get("ApiKey") match {
case Some(apiKey: String) => block(WithApiKeyRequest(apiKey, request))
case _ => Future.successful { BadRequest(Json.obj("errors" -> "ApiKey header needed")) }
}
}
private[this] def AuthentificationAction(authencationFunction: String => Future[_]) = new ActionFilter[WithApiKeyRequest] {
override protected def filter[A](request: WithApiKeyRequest[A]): Future[Option[Result]] =
authencationFunction(request.apiKey)
.map { _ => None } // Do not filter the request
.recover { case _ => Some(Unauthorized) }
}
private[this] def JsonAction[T](implicit reader: Reads[T]) = new ActionBuilder[ParsedJsonRequest] {
composeParser(BodyParsers.parse.json)
override def invokeBlock[A](request: Request[A], block: (ParsedJsonRequest[A]) => Future[Result]): Future[Result] = {
request.body.asInstanceOf[JsValue].validate[T].fold(
errors => Future { BadRequest(Json.obj("errors" -> JsError.toJson(errors))) },
(parsedJson: T) => block(ParsedJsonRequest(parsedJson, request))
)
}
}
}
It seems to work well but it's not perfect because I'm force to use the Any type in the case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) because it seems that I can't do that:
case class ParsedJsonRequest[T, A](parsed: T, request: Request[A])
Is it possible to do that ?
Do you think I can improve my solution ? How ?
My question is not about how to do Action composition. I understand how it works and I succeeded to write my ActionBuilders and my wanted composition.
My question is about how to improve my composition.
Thanks
Jules
Rather than making a new ActionBuilder for a JsonRequest, I would simply use the AuthentificationAction ActionBuilder and pass it a json BodyParser:
AuthentificationAction(parse.json) {
request => // Note that the request has type Request[JsValue]
doStuffWithJson(request.body)
}
Any action using this builder, will get a Request[JsValue] rather than a Request[AnyContent].

Play framework - pass parameter to Writes and Reads

Is there a way to pass parameter into a Writes to I will be able to control they way the JsValue is written?
This is how it looks right now:
implicit val myClassWrites = new Writes[MyClass] {
override def writes(l: MyClass): JsValue = Json.obj("a" -> l.a, "b" -> l.b)
}
But I want to do something like this:
implicit val myClassWrites = new Writes[MyClass] (extended: Option[Boolean]) {
override def writes(l: MyClass): JsValue = {
extended match{
case true => //do something
case false => //do something else
}
}
}
Is there an elegant way to achieve this? or something similar?
I managed do implement this need with Reads like this(dropping the implicit):
def myClassReads(c: String) : Reads[MyClass] = (
Reads.pure(c) and
(JsPath \ "a").read[String] and
(JsPath \ "b").read[String]
) (MyClass.apply _)
And then when I want to use the reads (usually in the controller when I want to validate the body of the request) I do:
request.body.validate[MyClass](MyClass.myClassReads("foo")).fold(
errors => //
myClass=> // do domething
)
So the Writes is still a mystery.
You can do this with implicit parameters. See code below:
case class Extended(b: Boolean)
object MyClass {
implicit def MyClassWrites(implicit extended: Extended): Writes[MyClass] = new Writes[MyClass] {
def writes(l: MyClass) =
if (extended.b) JsString("foo")
else JsString("bar")
}
}
And then use like this
implicit var extd = Extended(true)
println(Json.toJson(myClass)) // foo
extd = Extended(false)
println(Json.toJson(myClass)) // bar

Scala, Akka, Spray: How to validate json data before processing?

I can process this json when all the inputs are valid, i.e with valid keys (including case) and values. The next step is to validate keys and return 400 (Bad Request) if the keys or values are invalid. What is a good way to add this validation?
The API call
POST http://localhost:8080/api/v1/adsession
Content-Type: application/json
body {
"sessionId": "abcd123123123",
"serviceGroup": "1234",
"targetCode": {"zipcodes":"30096,30188","code2":"value2"}
}
Route handler
class AdSessionRoutes(services: Services)(implicit ec: ExecutionContext, log: LoggingContext) extends ApiRoute(services) {
implicit val timeout = Timeout(10 seconds)
val postSession = pathPrefix("adsession") & pathEnd & post
val route: Route = {
withService("adSession") { service =>
postSession {
entity(as[AdSession]) { adSession =>
log.info(s"Processing POST ${adSession}")
val future = (service ? CreateAdSession(adSession)).mapTo[AdSession]
onComplete(future) {
case Success(result) =>
complete(StatusCodes.Created, result)
case Failure(e) =>
log.error(s"Error: ${e.toString}")
complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
}
}
}
}
}
}
Model object
case class AdSession(
sessionId: String,
serviceGroup: String,
targetCodes: Map[String,String],
id: Option[String] = None)
object AdSessionJsonProtocol extends DefaultJsonProtocol {
implicit val adSessionFormat = jsonFormat4(AdSession)
}
entity(as[AdSession]) does map keys to case class fields, but I'm not sure how to catch those errors. I would like to capture those errors as well as add additional validations and return 400 with valid json error response.
I know this may be a little late but since akka-http-2.4.6, you can put the verification logic inside a case class. Check out this for info on how to do it.
Define your own read and write methods for AdSession like this:
object AdSessionJsonProtocol {
implicit object adSessionJsonFormat extends RootJsonFormat[AdSession] {
override def read(json: JsValue): AdSession = ???
override def write(obj: AdSession): JsValue = ???
}
}
Inside read function you can perform additional validation.
Another question is how to pass collected errors to Spray route. I would like to suggest you to wrap AdSession into Either[String, AdSession], for example:
postSession {
entity(as[Either[String, AdSession]]) { adSession =>
So, now your adSessionJsonFormat will looks like:
implicit object adSessionJsonFormat extends RootJsonFormat[Either[String, AdSession]] {
override def read(json: JsValue): Either[String, AdSession] = {
// json.asJsObject.fields access fields, perform checks
// if everything is OK return Right(AdSession(...))
// otherwise return Lift("Error Message")
}
override def write(obj: Either[String, AdSession]): JsValue = ???
}
But, I think it's possible to solve it in more elegant way using some implicit magic.

restart iterator on exceptions in Scala

I have an iterator (actually a Source.getLines) that's reading an infinite stream of data from a URL. Occasionally the iterator throws a java.io.IOException when there is a connection problem. In such situations, I need to re-connect and re-start the iterator. I want this to be seamless so that the iterator just looks like a normal iterator to the consumer, but underneath is restarting itself as necessary.
For example, I'd like to see the following behavior:
scala> val iter = restartingIterator(() => new Iterator[Int]{
var i = -1
def hasNext = {
if (this.i < 3) {
true
} else {
throw new IOException
}
}
def next = {
this.i += 1
i
}
})
res0: ...
scala> iter.take(6).toList
res1: List[Int] = List(0, 1, 2, 3, 0, 1)
I have a partial solution to this problem, but it will fail on some corner cases (e.g. an IOException on the first item after a restart) and it's pretty ugly:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
var iter = getIter()
def hasNext = {
try {
iter.hasNext
} catch {
case e: IOException => {
this.iter = getIter()
iter.hasNext
}
}
}
def next = {
try {
iter.next
} catch {
case e: IOException => {
this.iter = getIter()
iter.next
}
}
}
}
I keep feeling like there's a better solution to this, maybe some combination of Iterator.continually and util.control.Exception or something like that, but I couldn't figure one out. Any ideas?
This is fairly close to your version and using scala.util.control.Exception:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
import util.control.Exception.allCatch
private[this] var i = getIter()
private[this] def replace() = i = getIter()
def hasNext: Boolean = allCatch.opt(i.hasNext).getOrElse{replace(); hasNext}
def next(): T = allCatch.opt(i.next).getOrElse{replace(); next}
}
For some reason this is not tail recursive but it that can be fixed by using a slightly more verbose version:
def restartingIterator2[T](getIter: () => Iterator[T]) = new Iterator[T] {
import util.control.Exception.allCatch
private[this] var i = getIter()
private[this] def replace() = i = getIter()
#annotation.tailrec def hasNext: Boolean = {
val v = allCatch.opt(i.hasNext)
if (v.isDefined) v.get else {replace(); hasNext}
}
#annotation.tailrec def next(): T = {
val v = allCatch.opt(i.next)
if (v.isDefined) v.get else {replace(); next}
}
}
Edit: There is a solution with util.control.Exception and Iterator.continually:
def restartingIterator[T](getIter: () => Iterator[T]) = {
import util.control.Exception.allCatch
var iter = getIter()
def f: T = allCatch.opt(iter.next).getOrElse{iter = getIter(); f}
Iterator.continually { f }
}
There is a better solution, the Iteratee:
http://apocalisp.wordpress.com/2010/10/17/scalaz-tutorial-enumeration-based-io-with-iteratees/
Here is for example an enumerator that restarts on encountering an exception.
def enumReader[A](r: => BufferedReader, it: IterV[String, A]): IO[IterV[String, A]] = {
val tmpReader = r
def loop: IterV[String, A] => IO[IterV[String, A]] = {
case i#Done(_, _) => IO { i }
case Cont(k) => for {
s <- IO { try { val x = tmpReader.readLine; IO(x) }
catch { case e => enumReader(r, it) }}.join
a <- if (s == null) k(EOF) else loop(k(El(s)))
} yield a
}
loop(it)
}
The inner loop advances the Iteratee, but the outer function still holds on to the original. Since Iteratee is a persistent data structure, to restart you just have to call the function again.
I'm passing the Reader by name here so that r is essentially a function that gives you a fresh (restarted) reader. In practise you will want to bracket this more effectively (close the existing reader on exception).
Here's an answer that doesn't work, but feels like it should:
def restartingIterator[T](getIter: () => Iterator[T]): Iterator[T] = {
new Traversable[T] {
def foreach[U](f: T => U): Unit = {
try {
for (item <- getIter()) {
f(item)
}
} catch {
case e: IOException => this.foreach(f)
}
}
}.toIterator
}
I think this very clearly describes the control flow, which is great.
This code will throw a StackOverflowError in Scala 2.8.0 because of a bug in Traversable.toStream, but even after the fix for that bug, this code still won't work for my use case because toIterator calls toStream, which means that it will store all items in memory.
I'd love to be able to define an Iterator by just writing a foreach method, but there doesn't seem to be any easy way to do that.