Yogesh Parwani

Building awesome mobile experiences, one pixel at a time.

Yogesh Parwani

Building awesome mobile experiences, one pixel at a time.

Yogesh Parwani

Building awesome mobile experiences, one pixel at a time.

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.

void main(List<String> args) {
    final _result = add(1, 2);
    print(_result); // 3
}

int add(int numberOne, int numberTwo) => numberOne + numberTwo;

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.

int increment(int number) => number + 1;

int add(int numberOne, int numberTwo) => numberOne + numberTwo;

DateTime now() => DateTime.now();

bool isOdd(int number) => number % 2 == 1;

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.

typedef IntCallback = int Function(int number);

void main(List<String> args) {
    final _firstFnResult = _firstFunction();
    _firstFnResult();
    
    final _increment = add(1);
    print(_increment(2)); // 3
}

Function _firstFunction() {
    final _someValue = 'First function scope';
    
    void _secondFunction() {
        print(_someValue);
    }
    
    return _secondFunction;
}

IntCallback add(int a) {
    return (int b) {
        return a + b;
    };
}

_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.

void main(List<String> args) {
    // Not point free
    final _result = _numbers.map((e) => increment(e)).toList();
    print(_result); // [2, 3, 4, 5, 6]
    
    // Point free
    final _result2 = _numbers.map(increment).toList();
    print(_result2); // [2, 3, 4, 5, 6]
}

int increment(int number) => number + 1;
const _numbers = [1, 2, 3, 4, 5];

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.

void main(List<String> args) {
    // Non curried
    print(greet('Hello', 'Noah')); // Hello Noah
    
    // Curried
    print(curriedGreet('Hello')('Noah')); // Hello Noah
}

String greet(String salutation, String name) => '$salutation $name';

typedef StringCallback = String Function(String name);

StringCallback curriedGreet(String salutation) {
    return (String name) {
        return '$salutation $name';
    };
}

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.

import 'package:dartz/dartz.dart';

void main(List<String> args) {
    // Curry (dartz)
    
    final _curriedGreet2 = curry2<String, String, String>(greet);
    
    print(_curriedGreet2('Hello')('Noah')); // Hello Noah
}

String greet(String salutation, String name) => '$salutation $name';

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.

C Function(B) Function(A) curry2<A, B, C>(C Function(A, B) fun)

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.

import 'package:dartz/dartz.dart';

void main(List<String> args) {
    // Normal
    
    print(greet('Hello', 'Noah')); // Hello Noah
    print(greet('Bonjour', 'Charlotte')); // Bonjour Charlotte
    print(greet('Namaste', 'Rahul')); // Namaste Rahul
    print(greet('Hello', 'Liam')); // Hello Liam
    print(greet('Ciao', 'Isabella')); // Ciao Isabella
    print(greet('Ciao', 'Leonardo')); // Ciao Leonardo
    
    // Curried and Partial Application
    
    final _curriedGreet = curry2<String, String, String>(greet);
    
    final _hello = _curriedGreet('Hello');
    final _ciao = _curriedGreet('Ciao');
    final _namaste = _curriedGreet('Namaste');
    final _bonjour = _curriedGreet('Bonjour');
    
    print(_hello('Noah')); // Hello Noah
    print(_bonjour('Charlotte')); // Bonjour Charlotte
    print(_namaste('Rahul')); // Namaste Rahul
    print(_hello('Liam')); // Hello Liam
    print(_ciao('Isabella')); // Ciao Isabella
    print(_ciao('Leonardo')); // Ciao Leonardo
    
    // Demonstration
    print(_hello is Function); // true
    print(_hello); // Closure: (String) => String
}

String greet(String salutation, String name) => '$salutation $name';

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!


LET'S
CONNECT

LET'S
CONNECT

LET'S
CONNECT