This is my engineering org's philosophy as well. New engineers who have a java background tend to have some difficulty getting used to it, but all it takes is a light switch in their head.
For example, the other day there was a PR for creating a simple csv report and they wrote something like
val buffer = new StringBuffer()
buffer.add(header)
buffer.add("\n")
someList.forEach { item =>
val values: List[String] = someTransformation(item)
buffer.add(values.mkString(","))
buffer.add("\n")
}
buffer.toString
which is fine and readable, but it doesn't really seem like having side-effects (even though the scope was contained in the function and there weren't any worries about thread safety) aren't worth it over doing something like
val rows: List[String] = someList.map { item =>
val values: List[String] = someTransformation(item)
values.mkString(",")
}
(header :: rows).mkString("\n") + "\n" // I forgot whether they appended the string with a line break or not...