I am reading now Functional and Reactive Domain Modeling
. In the section 1.5, pg 30, there is an example:
credit(a, 100).flatMap(debit(_, 100))
=> Success(a.copy(balance = Balance(0 + 100))).flatMap(debit(_, 100))
=> debit(Account("a1", "John", .., Balance(100)), 100)
=> Success(a.copy(balance = Balance(100 - 100)))
=> Success(Account("a1", "John", .., Balance(0)))
I understand in general, what we do here: we substitute the functions with its implementations, for both credit
and debit
operations. In order to get finally the original result. But the exact example does not work.
For instance, with unit tests we use assertion to make sure it works. And as a result, to verify code, I can write expression and compare it. Here is a strange syntax. What does "=>" mean? There is no any comparison. How would you verify the correctness
Here is a full example of the code before this verification to be able to run it:
import java.util.{ Date, Calendar }
import scala.util.{ Try, Success, Failure }
def today = Calendar.getInstance.getTime
type Amount = BigDecimal
case class Balance(amount: Amount = 0)
case class Account(no: String, name: String,
dateOfOpening: Date, balance: Balance = Balance())
trait AccountService {
def debit(a: Account, amount: Amount): Try[Account] = {
if (a.balance.amount < amount)
Failure(new Exception("Insufficient balance in account"))
else Success(a.copy(balance = Balance(a.balance.amount – amount)))
}
def credit(a: Account, amount: Amount): Try[Account] =
Success(a.copy(balance = Balance(a.balance.amount + amount)))
}
object AccountService extends AccountService
import AccountService._
val t = today
val a = Account("a1", "John", t)
As you probably know the =>
is used in Scala for Function
types: A => B
is an alias for Function[A, B]
.
But in non-Scala context it sometimes appear in e.g. REPLs before printed expression result. I guess it is exactly, what we have here, except it also shows partial results (which is weird itself, as I haven't seen any REPL do it).
credit(a, 100).flatMap(debit(_, 100))
=> Success(a.copy(balance = Balance(0 + 100))).flatMap(debit(_, 100))
// credit substituted with its result
=> debit(Account("a1", "John", .., Balance(100)), 100)
=> Success(a.copy(balance = Balance(100 - 100)))
// debit substitutes with its result
=> Success(Account("a1", "John", .., Balance(0)))
// final result
For me it is a bit inconsistent/confusing notation for explaining referential transparency and/or how we arrived at the final result.
As for testing the result with assertion, in the end you get Try[Account]
. Even without any test framework matchers you could could check it as:
// given
val credit = credit(a, 100)
// when
val result = credit.flatMap(debit(_, 100))
// then
assert( result == Success(Account("a1", "John", .., Balance(0))) )
With e.g. ScalaTest assertion would look like
result shouldBe Success(Account("a1", "John", .., Balance(0)))
while Specs2 have something even more specific
result must beSuccessfulTry.withValue( Account("a1", "John", .., Balance(0)) )
(They all check basically the same thing, the difference will be report format on test failure.)