Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

for vs map in functional programming

I am learning functional programming using scala. In general I notice that for loops are not much used in functional programs instead they use map.

Questions

  1. What are the advantages of using map over for loop in terms of performance, readablity etc ?

  2. What is the intention of bringing in a map function when it can be achieved using loop ?

Program 1: Using For loop

val num = 1 to 1000
val another = 1000 to 2000
for ( i <- num )
{
  for ( j <- another) 
  {
    println(i,j)
  }
}

Program 2 : Using map

val num = 1 to 1000
val another = 1000 to 2000
val mapper = num.map(x => another.map(y => (x,y))).flatten
mapper.map(x=>println(x))

Both program 1 and program 2 does the same thing.

like image 320
Knight71 Avatar asked May 23 '15 20:05

Knight71


2 Answers

The answer is quite simple actually.

Whenever you use a loop over a collection it has a semantic purpose. Either you want to iterate the items of the collection and print them. Or you want to transform the type of the elements to another type (map). Or you want to change the cardinality, such as computing the sum of the elements of a collection (fold).

Of course, all that can also be done using for - loops but to the reader of the code, it is more work to figure out which semantic purpose the loop has, compared to a well known named operation such as map, iter, fold, filter, ...

Another aspect is, that for loops lead to the dark side of using mutable state. How would you sum the elements of a collection in a for loop without mutable state? You would not. Instead you would need to write a recursive function. So, for good measure, it is best to drop the habit of thinking in for loops early and enjoy the brave new functional way of doing things.

like image 197
BitTickler Avatar answered Nov 15 '22 03:11

BitTickler


I'll start by quoting Programming in Scala. "Every for expression can be expressed in terms of the three higher-order functions map, flatMap and filter. This section describes the translation scheme, which is also used by the Scala compiler." http://www.artima.com/pins1ed/for-expressions-revisited.html#23.4

So the reason that you noticed for-loops are not used as much is because they technically aren't needed, and any for expressions you do see are just syntactic sugar which the compiler will translate into some equivalent. The rules for translating a for expression into a map/flatMap/filter expression are listed in the link above.

Generally speaking, in functional programming there is no index variable to mutate. This means one typically makes heavy use of function calls (often in the form of recursion) such as list folds in place of a while or for loop.

For a good example of using list folds in place of while/for loops, I recommend "Explain List Folds to Yourself" by Tony Morris. https://vimeo.com/64673035

If a function is tail-recursive (denoted with @tailrec) then it can be optimized so as to not incur the high use of the stack which is common in recursive functions. In this case the compiler can translate the tail-recursive function to the "while loop equivalent".

To answer the second part of Question 1, there are some cases where one could make an argument that a for expression is clearer (although certainly there are cases where the opposite is true too.) One such example is given in the Coursera.org course "Functional Programming with Scala" by Dr. Martin Odersky:

for {
  i <- 1 until n
  j <- 1 until i
  if isPrime(i + j)
} yield (i, j)

is arguably more clear than

(1 until n).flatMap(i =>
  (1 until i).withFilter(j => isPrime(i + j))
    .map(j => (i, j)))

For more information check out Dr. Martin Odersky's "Functional Programming with Scala" course on Coursera.org. Lecture 6.5 "Translation of For" in particular discusses this in more detail.

Also, as a quick side note, in your example you use

mapper.map(x => println(x))

It is generally more accepted to use foreach in this case because you have the intent of side-effecting. Also, there is short hand

mapper.foreach(println)

As for Question 2, it is better to use the map function in place of loops (especially when there is mutation in the loop) because map is a function and it can be composed. Also, once one is acquainted and used to using map, it is very easy to reason about.

like image 25
scenefinale Avatar answered Nov 15 '22 05:11

scenefinale