When I originally selected the name for this blog back many years ago, I had the topic of this post in mind. For me, dataflow programming has always resembled functional programming and many concepts natural in functional programming translate very well into dataflow world. The key concept in functional programming is the idea that any expression can be passed around as a value within the application. Translating this idea into dataflow programming would mean that one could pass around subdiagrams in your dataflow diagram.
Passing around function references is nothing new to dataflow programming. For example LabVIEW has supported references to named functions for a long time. However, passing around references to named functions isn’t very interesting as the expressivity of named functions doesn’t adapt to the context.
To take advantage of the full power of functional programming the concept of closures needs to be introduced to the dataflow programming. A closure is a function definition or reference together with an environment that defines some of the input values to the function. When passing around functions in a functional programming, one actually passes around closures i.e. functions with their partially predetermined environment.
Let’s look how this concept would adapt to synchronous dataflow programming languages such as LabVIEW. In the image below we illustrate the construction of a closure of a named function “add”.
Constructing a closure in functional synchronous dataflow programming, passing it around as a value and calling it using an apply method
We introduce a new closure structure (on the left) that constructs a closure of a function inside the structure. The environment of the closure if determined by connecting wires from outside the diagram to one or more of the input terminals of the function within the closure structure. In the example above we have constructed a closure of an “add” function by fixing it’s first input to constant value of 3. Naturally the value didn’t have to be constant, it could as well be any value flowing in the wire. The output of the closure structure returns an expression representing a function definition of f: f(x)=x+3.
The closure can be called by passing the closure to an apply structure (on the right). The apply structure is a function that is expecting a function definition as an input. It allows the programmer to bind the unbound terminals of the function. Execution of the apply structure leads into evaluation of the function determined by the function input terminal (green terminal) by binding the unbound terminals to defined values at the time of execution of the apply structure and then evaluating the function.
In the above example, a closure representing a partial function is first constructed by binding number three to one of the terminals of the add function and then passing this closure forward to an apply structure. The apply structure then binds the other terminal to value seven and evaluates the function. The result of the evaluation is 3+7 = 10.
Using closure in the above is not very useful and the example is provided for illustrative purposes. We will in the future posts discuss more the design patterns of using functional synchronous dataflow programming. We will also introduce closures of anonymous functions defined in-place on the diagram. The principles introduced here can be extended into flow-based programming paradigm and I will discuss this in a future post.