Let’s revisit what you’ve said earlier:
I have:
- removed arms of control flow
- made it clear which variables are in scope (all are to the left of
<-
operator in for comprehension) - replaced everything with for comprehension
I don’t see a difference about shadowing. There’s the same number of variables in scope in every approach.
Every line required either nestError
or returnEarly
, but:
- in case of nested flatMaps I need flatMap for every nest level (i.e. every Either)
- in case of magical
?
operator I need to insert that operator for every Either - I could renamenestError
to?
if that operator is so cool - if you forget to do either
nestError
orreturnEarly
on some line thenresultNested.joinLeft
won’t compile, so you’ll immediately know you’ve forgot something
Also, you’ve forgot to wrap results in Right, i.e. there should be return Right(Response(...
instead of return Response(...
After renaming nestError
to ?
the contest would be between hypothetical:
def handleTransfer(req: TransferRequest): Either[Err, Response] = {
val u1 = req.user1.lookup(db).?
val u2 = req.user2.lookup(db) match {
case Left(e) => return Right(Response(s"Cannot find ${req.user2}: $e"))
case Right(u) => u
}
val depositor = u2.checkAccess(u1) match {
case Left(e) => return Right(Result(s"$u2 has not given you deposit access"))
case Right(dep) => dep
}
val withdrawal = u1.withdraw(req.amount).?
val conf = depositor.accept(withdrawal).?
Result(s"You transferred $withdrawal to $u2, confirmation number $conf")
}
and already working:
def handleTransfer(req: TransferRequest): Either[Err, Result] = {
for {
u1 <- req.user1.lookup(db).?
u2 <- req.user2.lookup(db).earlyResult { e =>
Result(s"Cannot find ${req.user2}: $e")
}
depositor <- u2.checkAccess(u1).earlyResult { _ =>
Result(s"$u2 has not given you deposit access")
}
withdrawal <- u1.withdraw(req.amount).?
conf <- depositor.accept(withdrawal).?
} yield {
Result(s"You transferred $withdrawal to $u2, confirmation number $conf")
}
}.joinLeft