Actor / STM models and database persistence are somewhat orthogonal - you can easily have one without the other, and I think there is a danger of confusing the two.
Achieving Durability (the D in ACID) is extremely complex in a transactional setting, and particularly in a distributed setting where you have actors / processes being co-ordinated by message passing. You get into thorny issues like the Byzantine Generals Problem.
As a result, I think there is always going to be some degree of tailoring the solution to meet your specififc persistence requirements. There are no "one size fits all" solutions.
Worth looking at (Clojure perspective):
Scala doesn't require an explicit return type on all functions, just recursive ones. The reason for that is that Scala's type inference algorithm is (something close to) a simple scan from beginning to end that is incapable of lookahead.
This means that a function like this:
def fortuneCookieJoke(message: String) = message + " in bed."
doesn't need a return type, since the Scala compiler can clearly see, without using logic variables or looking at anything other than the method's parameters, that the return type must be String
.
On the other hand, a function like this:
def mapInts(f: (Int) => Int, l: List[Int]) = l match {
case Nil => Nil
case x :: xs => f(x) :: mapInts(f, xs)
}
will cause a compile-time error, because the Scala compiler cannot see, without using either lookahead or logic variables, exactly what the type of mapInts
is. The most it could say, if it were smart enough, is that the return type is a supertype of List[Nothing]
, since Nil
is of that type. That doesn't give it anywhere near enough information to accurately determine the return type of mapInts
.
Please note that this is specific to Scala, and that there are other statically typed languages (most of the Miranda/Haskell/Clean family, most of the ML family, and a few scattered others) that use much more comprehensive and capable type inference algorithms than Scala uses. Also, be aware that this is not entirely Scala's fault; nominal subtyping and whole-module type inference are fundamentally at odds with each other, and the designers of Scala chose to favor the former over the latter for the sake of Java compatibility, while the "purer" statically typed functional languages were mostly designed with the opposite choice in mind.
Best Answer
The general approach is instead of mutating a variable in a loop, put the values into collections and run a series of operations on those collections. Note that most of the time, functional algorithms don't need index variables. Look for mathematical relationships that allow you to factor those out. For example:
Just because it's a series of operations on a collection doesn't mean you have to cram all the operations together one after another. Don't forget to break up lines, name intermediate results, and use named functions instead of lambdas where appropriate. The compiler essentially optimizes out
numberOfRows
anda
, but putting them in there adds a lot of readability for humans.Don't avoid for comprehensions just because of their superficial resemblance to mutated for loops. They are often the key to better readability.
Note I moved the
println
to outside the for comprehension. Isolating side effects like that can often make it easier to see a pure functional solution for the rest of the problem.Also, as an aside, you don't need the
augmentString
. It is implicitly added for you.