Mar 22, 2022
7 min read
Arity, Closure, Currying, Partial Application & more in Dart [Functional Programming — Part 2]
I have noticed the term’s arguments and parameters used interchangeably. So before starting, let’s understand the difference.
Parameter: The parameter is a part of the function declaration.
Argument: The argument is the values passed to the function. It is a part of the function call.
Here, in the add
function declaration, numberOne
and numberTwo
are the parameters. On line no. 2; 1 and 2 are the arguments passed to the add function.
In a nutshell, parameters are the variables that take in the values, and arguments are the values passed in.
Arity
Arity is the number of arguments a function takes.
The arity of the increment
function is 1. Function taking a single argument is known as a unary function.
The arity of add
is 2. Function taking two arguments is known as a binary function.
The arity of now
is 0. Function taking no arguments is known as a nullary function.
The arity of isOdd
is 1, and hence it is a unary function. isOdd
is also a predicate function as it returns a bool. A predicate function is a function that returns a boolean value.
Functions & Call stack
Let’s understand what stages a function goes through. First, it is declared. This includes the function name, its parameters and the function body. Each function has a scope that maintains the state of all the variables for the function. Once the function is invoked (i.e. called), it gets added to the call stack.
The call stack is a list of function invocations. It uses Stack, which follows the LIFO (Last in, First out) principle. When the function returns, it gets removed from the call stack. As Dart is a single-threaded language, the call stack helps with maintaining the history for all the function calls.
Closure
As seen, every function has a scope. So, when a function is defined into another function, that inner function remembers the scope of the outer function even if the outer function is executed and no longer available. This is known as Closure.
_secondFunction
is an inner function declared inside the _firstFunction
. On line 5, we call _firstFunction
, and it returns the _secondFunction
, which is stored in the_firstFnResult
variable. Call stack removes the _firstFunction
when it returns the value. Let’s see the body of _secondFunction
. It is accessing the _someValue
variable, which is in the _firstFunction
scope. If we call _firstFnResult
, it will print the value on the console as it maintains the state. It is because of closure.
Let’s take another example. We call the add
function on line no. 8. The function add takes a single argument and returns a function that takes a single argument. Now, as the function remembers the first argument(1 in our case), we need only to pass the second argument, and it returns the sum.
Partial Application
A function applied to some of its inputs is a Partial Application. Closure helps us to achieve partial applications. The idea behind the partial application is to lock in some of the parameters, so we don’t have to pass them repeatedly. Let’s look at the add
function in the image attached in the Closure section. The add
function needs two arguments in series to return the result, but we have passed a single argument and assigned it to the _increment
variable. We have not given all the arguments; we have partially applied the function.
Point-free style
We can think of points like the arguments passed into a function. Point-free style defines a function without needing to pass in the arguments explicitly.
On line no. 3; e is the point passed explicitly to the increment function. To convert it into a point-free style, we remove the explicit argument.
Point-free style, if not overdone, makes code concise and readable. It is more of a choice and not a must.
Currying
Breaking down a multi-argument function into a series of single-argument functions is currying. A curried function takes a single argument at a time and returns a function that takes a single argument and so on until all the arguments are passed.
greet
is a function that takes two arguments and returns a String.
A curried version of greet would look like curriedGreet
. It takes in a single argument and returns a function that takes a single argument and returns the result.
If this is the first time you’re looking at a curried function, the implementation of _curriedGreet
might look a bit strange. Also, you might have noticed that it’ll have a deeply nested structure as the number of arguments increases. So is there a way to overcome these problems?
Welcome, dartz! We’ll use the dartz
package to implement the FP concepts in dart. Let’s convert the greet function into a curried function using dartz
.
curry2
is a function provided by the dartz
package, which converts the two-argument function into a curried function. Yep, You guessed it! curry3
is to convert a three-argument function into a curried function, curry4
for four-argument and so on till curry6
.
Above is the curry2
declaration that takes 3 generics. A
and B
are the first and second arguments for the function you want to curry, respectively. C
is the result. Our greet
function takes in two String and returns a String; hence we pass 3 String as generics.
Currying vs Partial Application
Partial application and currying might look identical, but they’re different concepts. The partial application can take more than one argument at a time, whereas the curried function always has to return a unary function (single-argument function). Curried functions are used to create partial applications, but all partial applications are not curried functions.
Example
Now that we know these concepts let us bring them all together and create a program that greets people in their language.
We have the same greet
function, which takes two arguments and returns a String. The first argument is the salutation which can be different based on the user’s origin, and the second is the user’s name. If we have 50 people from India, we have to pass the hello argument 50 times which seems redundant. Let’s use the concepts we have learned and apply them here.
We use the curry2
from the dartz
package to turn our greet
function into a curried function at line 15. Using this, we can create several salutation partial applications and later use them by only passing the user’s name.
We create partial applications from lines 17 to 20 and pass in the salutation argument. Now _hello
is a partial application as it is not fully executed yet. In our scenario, it is a function expecting the second argument, the user’s name. Look at lines 30 and 31; when we print _hello
, we get a function that expects a string as an argument.
Now we are all set, we have these partially applied functions ready to be used. If there comes a request for new salutation support, we just need to create a new partial application. To use these, we pass the user’s name on lines 22 to 27, giving us the results. Even though we have not passed the salutation argument, the _hello
and other functions remember them because of Closure.
Final Thoughts
Phew! that was something. We started with basics: arguments vs. parameters, covered the function’s Arity and how functions and call stacks work together. Then we explored Closures, Partial application, Currying, and how they work together. We also saw the point-free style and how it could lead towards a readable codebase. Finally, we had one example demonstrating these concepts together. All this might look a bit strange, but once we start using these, it becomes second nature. Try and see if you can implement this in your existing codebase. Like anything else, the more you practice the more natural it will feel.
In the following article, we’ll bump our level with functional composition and combine those techniques with what we have learned today to solve complex problems.
Awesome! Pat yourself on the back for reaching till the end. I hope I added some value to the time you invested. Find out more examples on the GitHub repository, and reach out on Twitter or LinkedIn for suggestions/Questions or any topic you’d like me to cover. You can support it by clapping👏, and thanks for reading :) Follow for more😄
Until next time, folks!