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.

Tomer Ben David (8) [Avatar] Offline
#1
val cust = getCustomer(..) verifyCustomer(cust).map(c => openCheckingAccount(c, date)) .getOrElse(throw new Exception("Verification failed for customer"))

so the side effect is last. usually in my actual work I see side effects also in middle of chain.

enother example from book:

val generateAuditLog: (Account, Amount) => Try[String] = //..
val write: String => Unit
debit(source, amount)
.flatMap(b => generateAuditLog(b, amount))
.foreach(write)

again side effect is last operation. however in real life side effect is also in middle. It sounds simple to me when side effect is last but how to handle it when its in the middle?

for example:

1. receive request
2. audit that received request
3. validate request.
4. send event (side effect) that passed validation.
and so on...
.
.


so side effects are eventually returning Unit and its important that they return Unit because this is a big sign - this is a side effect. but if they are in the middle of the chain how can they return Unit? if we have them not returning unit they will loose the big sign this is a side effect. How to handle these cases while preserving the ordering? I mean ofcourse I can aggregate all the side effects and run them at the end but this would miss the ordering at any point in time I first received the request and only then audit the event i then validated the request and only then sent an event.

So i'm bewildered at how to handle such a scenario, does the book handles it? I only read up to this point (~page 34) and i'm not sure whether i'm referring to a point that is handled in the following book pages or not. and if not then how should I be handling such scenarios.
Debasish Ghosh (116) [Avatar] Offline
#2
Hello -

Actually there are quite a few options. Let me discuss one of them using which you can have a nice composition of pure computation as well as ones that can be side-effecty. The solution is to wrap your computation in a monad. In this use case I am using Try, which is a monad. Try is useful since all of the operations that we are trying to compose together can fail ..

def receiveRequest(body: Int): Try[Request]
def audit(r: Request): Try[Unit]
def validate(r: Request): Try[Boolean]
def sendEvent(r: Request): Try[Unit]

for {
  r <- receiveRequest(100)
  _ <- audit(r)
  _ <- validate(r)
  _ <- sendEvent(r)
} yield (())


Note the side-effecty ones have the signature Try[Unit], Unit indicates the side-effect and the Try indicates that it can fail. There are many other advanced options e.g. using Free Monads that I discuss later in the book. Pls beware that the above code is just a rough one and I don't have a compiler right now. Please feel free to get back if u need more details.

Thanks.
Tomer Ben David (8) [Avatar] Offline
#3
Thanks. It answers what I was looking for.
Tomer Ben David (8) [Avatar] Offline
#4
So I hope that monads and free monads will help me prove the correctness of my code even though it involves intermingled chaining of pure function calls and function calls which have side effects. This would be very cool. So I'll continue to read thanks smilie