In a lot of web articles, functional programming is presented as avoiding all kinds of variables reassignment and so promoting only "final" variables at least for especially just better reading.

Most of them are taken the sample of a poor loop with a counter variable incrementing. (like the famous i++ or x = x + 1. Here an article of Uncle Bob illustrating it: FP Episode 1

Hence, these articles signal that counting on mutable variables very often leads to side effects, and especially prevent what we call "Referential transparency" and therefore, makes harder to construct a program running on multi-threads or better, multi-processors.

My question is: As we all know, i++ is generally a thread LOCAL variable, so no issues can occur even with concurrent processing.

Why to choose an example like a loop with local variable as a drawback of assignments, and permit directly to conclude that concurrency programming is risking?? These both things are strictly unrelated to me.

Why not, in order to be more clear, choose a reassignment of global variable (or field object) that is clearly the enemy of concurrent programming without overusing all boilerplates of locks as Java does.

I really think that this loop sample isn't the best illustration to transmit benefits of functional programming to imperative programmers guys.

Furthermore, it leads to confusion with "noob" functional programmers since Scala for instance uses a lot of the while loop pattern as in List.scala class:

override def take(n: Int): List[A] = {
    val b = new ListBuffer[A]
    var i = 0
    var these = this
    while (!these.isEmpty && i < n) {  
      i += 1   // reassignment here
      b += these.head
      these = these.tail
    }
    if (these.isEmpty) this
    else b.toList
  } 
有帮助吗?

解决方案

I think Odersky himself said that they aim for API's to be functional but internal code is what is most optimal for a specific implementation. So you probably should not search Scala library internals for "good use of Scala" or "great examples of FP".

Using mutable state to hold indexes (for example) is also really error-prone. So you should aim to use operations to whole collections (filter/map/flatMap etc) so you never need to worry about stuff like "index out of bounds". Still, these operations often cause lot's of temporary/intermediary collections to be created, and so they cause extra garbage collecting. This usually doesn't matter for 99% of programs but again, these are optimized as much as possible inside Scala library internals.

So yes, there's a place for everything but practicing to "survive" with as little mutable state as possible is good practice for single-threaded programs too, because of fewer possible places for bugs, easier testability, better readability.

其他提示

In a simple loop, there's no problem--it isn't ever a problem of concurrency, and you can probably keep track of the variables. Well, maybe.

// Take the first n items that pass p
def takeGood(n: Int)(p: A => Boolean): List[A] = {
  val b = new ListBuffer[A]
  var these = this
  var i = 0
  while (!these.isEmpty && i < n) {
    i += 1
    if (p(these.head)) b += these.head
    these = these.tail
  }
  b.toList
}

Um, except this doesn't work--we've incremented i on every loop, not just on the ones we take.

If you use recursion, it becomes more obvious, at least, what you're doing:

def takeGood[A](these: List[A], n: Int)(p: A => Boolean)(b: ListBuffer[A] = new ListBuffer[A]): List[A] = {
  if (these.isEmpty || n <= 0) b.toList
  else if (p(these.head)) takeGood(these.tail, n-1)(p)({ b += these.head; b })
  else takeGood(these.tail, n)(p)(b)
}

So there can be advantages to using functional style even when concurrency is not in play: sometimes (especially with loops) it makes the looping much more explicit and therefore reduces the chance of error.

Concurrency brings additional advantages, as being consistent but out of date is usually much better than being inconsistent or deadlocking. But that's not what is shown in a while-loop with iterator.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top