Wednesday, May 26, 2010

Return value of a block

A common misunderstanding is that a code block (without parameters) is a function. That is not the case. A code block is a sequence of statements that are executed and result the last statement is returned. That sounds like a Function0, however, if the block is passed to a method/function only the last statement will be returned to the function/method. If that method/function expects a function as the parameter the last statement maybe returned as a function not a value, this means that the block itself is not a function.
  1. scala> var count = 0                                                                                                                         
  2. count: Int = 0
  3. // the last statement is returned as a function so count
  4. // is incremented only one during the creation of the function
  5. scala> List(1,2,3,4).map{count += 1;_ + 1}
  6. res9: List[Int] = List(2, 3, 4, 5)
  7. scala> count
  8. res10: Int = 1
  9. // now the count increment is within the function
  10. scala> List(1,2,3,4).map{i => count += 1;i + 1}
  11. res11: List[Int] = List(2, 3, 4, 5)
  12. scala> count
  13. res12: Int = 5

The previous example demonstrates a Gotcha if I ever saw one. Map expects a function so the block essentially constructs a function. The last statement being the function. The first line count += 1 executed only once because it is part of creating the function not part of the resulting function. This is equivalent to:
  1. scala> val x = {count += 1 ; i:Int => i +1}
  2. x: (Int) => Int = < function1>
  3. scala> List(1,2,3,4).map(x)
  4. res15: List[Int] = List(2, 3, 4, 5)

Beginning a block with the parameter list signals that the entire block is a function.

Rule of thumb: Functions with placeholder parameters should be a single statement.

6 comments:

  1. Whoa, I had no idea...

    "Rule of thumb: Functions with placeholder parameters should be a single statement."

    That should be _without_ placeholder params, right?

    ReplyDelete
  2. I guess that depends on what I mean by placeholder params :).

    {_.toString} has a placeholder param (by my definiition) and my rule is that it should always be a single statement. otherwise use explicit parameters like:

    {x => x.toString}

    ReplyDelete
  3. I wonder why... is just a bug or it has some purpose?

    ReplyDelete
  4. This is not a bug. It is an effect of consistency. As I mention in the post a block is a sequence of statements and only the last statement is used in the return. The {x => xxx} is actually a special construct of:

    val x = (m:Int) => {println("hi"); m + 1}

    The { x=> xxx} construct is nice when passing a function as a paramter:

    method(x => {x+2})

    vs

    method {x=> x+2}

    It is a case of reducing syntactic noise.


    In summary all the choices separately make sense but when combined obviously has an unexpected side effect.

    ReplyDelete
  5. I think there is a case when the whole block will be executed multiple times, and that is when you use a "call by name" argument. E.g.

    scala> var count=0
    count: Int = 0

    scala> def g(x: =>Unit) = { for (i<-1 to 4) {x} }
    g: (x: => Unit)Unit

    scala> g{count+=1; println("hi")}
    hi
    hi
    hi
    hi

    scala> count
    res13: Int = 4

    ReplyDelete
  6. I'm just starting to learn Scala and this kind of details are perplexing. Nevertheless, I think I found a place where it makes sense:

    scala> List(5,6,7,8).map{var num = 0; i:Int=> {num = num+1; num.toString + "_" + i}}

    res44: List[java.lang.String] = List(1_5, 2_6, 3_7, 4_8)

    I'm using the part that is executed only once to do some initialization, and the rest of the block (the function) is executed once per element in the list, using the initialized values.

    ReplyDelete