The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

401279 (2) [Avatar] Offline
#1
Hi again,

I have a question regarding composition of commands in a monadic way and error handling.

Let's consider this piece of code from chapter 8:
def transfer(from: String, to: String, amount: Amount): Command[Unit] = 
  for {
    _ <- debit(from, amount)
    _ <- credit(to, amount)
  } yield ()

which creates a new command by composing two other commands.

My question is about what happen when the second command, credit(to, amount), fails. From what I understand (which get confirmed with soe tests), the result is the following:
- The composed command (transfer) fails
- but the first composing command (debit(from, amount)) get executed as it happens before the failure

If this correct in terms of monadic programing logic, it is not very intuitive from a domain point of view. What I would expect in a banking context is that no command get executed if the whole failed: if we do a transfer and the credit operation fails, the debit operation that follows should not occur.

Am I correct or did I miss something?

And if I am correct, do you have some suggestion on how to obtain such a behavior, that is to have monadicly-composed commands that get validated as a whole before being executed?
From what I understand, I would say that this requires to implement some sort of session-commit-rollback mechanism at the interpreter level...

Best regards,
Marc-Antoine
Debasish Ghosh (116) [Avatar] Offline
#2
The code fragment illustrates monadic composition and not transaction handling. Typically if you want to ensure atomic execution then you need to execute the whole sequence of monadic statements within a transaction. You need a separate combinator to handle transactions. e.g. if you use Slick then you can use something like the following ..

def transfer(from: String, to: String,&nbsp;amount: Amount): Command[Unit] = 
  (for {
    _ <- debit(from, amount)
    _ <- credit(to, amount)
} yield ()).transactionally


where the transactionally combinator will ensure database level atomicity of operations.

HTH.