Branching with multiple Try
Having a Try[Unit] is completely ok - it models the fact that there may be exceptions on a side effecting operation in a type. That your code looks ugly is not because your monad is wrong but because you used it wrong. You already started in modeling your application with monads, why just stopping in the middle?

You can easily restructure your monad a little bit:

def run(): Try[Unit] = {
  val res = for {
    fields &lt;- getFields()
    values &lt;- getValues()
  } yield {
    if (values.isEmpty)
      Success(())
    else for (file &lt;- createTmpFile(fields, values))
      yield runProcessWithFile(file)
  }
  res.flatten
}


What we see here is a monad.map(f).flatten which is the same as monad.flatMap(f) which is the same as for (m &lt;- monad) yield f(m). Applying this fact to your code snippet results in:

def processTmpFile(as: List[A], bs: List[B]): Try[Unit] =
  if (values.isEmpty)
    Success(())
  else for (file &lt;- createTmpFile(fields, values))
    yield runProcessWithFile(file)

def run(): Try[Unit] = {
  for {
    fields &lt;- getFields()
    values &lt;- getValues()
  } yield processTmpFile(fields, values)
}


Operating on a deeper level of the monad (what you did with your condition) means that you can either nest the monad and unpack it later at the end or that you can unpack it immediately after the nesting happened (that is what a for comprehension does).