Adventures in HttpContext All the stuff after 'Hello, World'

First Class Function Example in Scala and Go

Go and Scala both make functions first-class citizens of their language. I recently had to recurse a directory tree in Go and came across the Walk function which exemplifies first-class functions. The Walk function talks a path to start a directory traversal and calls a function WalkFunc for everything it finds in the sub-tree:

func Walk(root string, walkFn WalkFunc) error 

If you’re coming from the Kingdom of Nouns you may assume WalkFunc is a class or interface with a method for Walk to call. But that cruft is gone; WalkFunc is just a regular function with a defined signature given its own type, WalkFunc:

type WalkFunc func(path string, info os.FileInfo, err error) error

Why is this interesting? I wasn’t surprised Go would have a built-in method for crawling a directory tree. It’s a pretty common scenario, and I’ve written similar code many times before. What’s uncommon about directory crawling is what you want to do with those files: open them up, move them around, inspect them. Separating the common from the uncommon is where first-class functions come into play. How much code have you had to write to just write the code you want?

Scala hides the OOP-ness of its underlying runtime by compile-time tricks, putting a first-class function like:

val walkFunc = (file: java.io.File) => { /* do something with the file */ }

into a class of Function1. C# does something similar with its various function classes and delegate constructs. Go makes the interesting design decision of forcing function declarations outside of structs, putting an emphasis on stand-alone functions and struct extensibility. There are no classes in Go to encapsulate functions.

We can write a walk method for our walkFunc in Scala by creating a method which takes a function as a parameter (methods and functions have nuanced differences in Scala, but don’t worry about it):

object FileUtil {
  def walk(file: File, depth: Int, walkFunc: (File, Int) => Unit): Unit = {
    walkFunc(file, depth)
    Option(file.listFiles).map(_.map(walk(_, depth + 1, walkFunc)))
  }
}

In our Scala walk function we added a depth parameter which tracks how deep you are in the stack. We’re also wrapping the listFiles method in an Option to avoid a possible null pointer exception.

We can tweak our walkFunc and use our Scala walk function:

import FileUtil._
val walkFunc = (path: File, depth: Int) => { println(s"$depth, ${path}") }
walk(new File("/path/to/dir"), 0, walkFunc)

Because typing (File, Int) => Unit is somewhat obscure, type aliases come in handy. We can refactor this with a type alias:

type WalkFunc = (File, Int) => Unit

And update our walk method accordingly:

def walk(file: File, depth: Int, walkFunc: WalkFunc): Unit = { ... }

First class functions are powerful constructs making code flexible and succinct. If all you need is to call a function than pass that function as a parameter to your method. Just as classes have the single responsibility principle functions can have them too; avoid doing too much at once like file crawling and file processing. Instead pass a file processor call to your file crawling function.