Using Blocks in Julia

By: Phillip David

Re-posted from: http://phrodod.github.io/blog/2014/09/21/using-blocks-in-julia/

Blocks, like lambdas, are a way of defining anonymous functions in Julia. This
post details how to write code that uses blocks. Let’s start with a canonical
Julia example:

Canonical example using map
1
2
3
4
5
6
7
8
9
julia> f(x) = 2x
f (generic function with 1 method)

julia> map(f, [2,4,6,8])
4-element Array{Int64,1}:
  4
  8
 12
 16

In this example, I pass a function to the map function, which applies the
function supplied as the first argument to each element of the array passed as
the second argument. Here, the function simply doubles the values.

I can do this more simply by using a lambda here:

Canonical lambda example using map
1
2
3
4
5
6
julia> map(x -> 2x, [2,4,6,8])
4-element Array{Int64,1}:
  4
  8
 12
 16

Here, the function is simply defined in-line. For a simple function, this is
fine. But if the function gets more complicated, the lambda form would
hide the connection between the array and the map call. Traditionally, that’s
when you’d create a separate function. But with Julia, we have another option.
The block allows you to provide the function between a do and an end
following the call to map. It looks like this:

Canonical block example using map
1
2
3
4
5
6
7
8
julia> map([2,4,6,8]) do x
    2x
end
4-element Array{Int64,1}:
  4
  8
 12
 16

Notice that in this example, the call to map looks like it only takes one
argument. But the block following the function call provides the function that
gets passed as the first argument. Note that the argument to the block gets
defined on the same line as the do statement, but the remaining portion of the
block goes on following lines until you reach the end statement.

By allowing blocks instead of allowing only simple lambda expressions, Julia
allows you to perform significant work inside the block. The most obvious
example is file manipulation. The open statement has a version that takes a
function as an optional first argument. If you call that version, the file is
opened, passes the file handle to the function, then closes the file at the
end. This allows the open function to take care of all of the nasty edge cases
like end of file, file read errors, etc, along with normal file activity. All
I need to do is to use the opened file handle.

Using a block to write file contents
1
2
3
open("myfile.dat","w") do f
   write(f,"hello world")
end

In this case, my local variable within the do block is f, the file handle.
I then write some data to it. I don’t need to worry about closing the file
when I’m done, because I’m using the special form of the open statment. When
handling large files (too big to fit in memory), it’s often desirable to
process them a line or a few lines at a time. With the special open structure,
you can do this, yet still allow Julia to clean up behind you.