Getting Started in Julia

Learning the Julia programming language

Using Blocks in Julia

| Comments

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.