401279 (2) [Avatar] Offline
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,
Debasish Ghosh (111) [Avatar] Offline
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.