Partial function application refers to calling a multiple parameter function with less than its total number of parameters. This results in a new function taking the remaining number of parameters. Simple functions created from partial function application are very useful and often require much less syntax than an anonymous function equivalent. In Haskell, partial function application is the norm.
Before we look at partial function application, we have to discuss currying. Currying transforms a function that takes multiple parameters into a chain of one-parameter functions. In Haskell, all functions are curried. For example, a function that takes two parameters is actually two one-parameter functions.
Let’s take a look at an example using GHCi, Haskell’s repl.
Here we define
sum, a two-parameter function (in Haskell,
let can be used to define a variable and function definitions don’t use parentheses or commas to delimit parameters). We then display its type signature using GHCi’s
type command. The letter
a represents a value of some type.
Num => a is a constraint on the type of
a. It requires
a to be an instance of the
Num typeclass, i.e., a number.
-> is right-associative.
a -> a -> a more explicitly, is
a -> (a -> a); a function that takes a number and returns a function that takes a number and returns the sum of the two numbers.
Partial Function Application
Calling a function is also known as applying a function; you apply a function to some arguments. If you call a multi-parameter function with less than its total number of parameters, then you are partially applying the function.
Here we call
sum, our previously defined two-parameter function, with only one argument and receive a new function. Haskell infers the type of this new function to be a function that takes an integer and returns an integer. This is because we called it with an integer. We then call this new function and receive the sum of two integers.
Let’s look at a few more examples.
This first example partially applies
* (multiplication) to create a one-parameter function that will double an integer. The next example
(2 ^) is called a section. A section is a partial application of a function written infix style (when an operator is written between its operands). This
^ (integer exponentiation) section returns a function that will return the number 2 raised to a given power.
This next example partially applies
map to get a function that takes a list of numbers, and returns them transformed to a new list of each number incremented by 1. Next is an equivalent implementation using an anonymous function. The anonymous function, while more familiar to most programmers, is more verbose and less elegant than using partial function application.
This last example partially applies a three-parameter function
foldl (commonly known in other programming languages as
reduce). This creates a new function that takes a list of integers and returns an integer (their sum).
The following function calculates the product of a list of integers.
prod function takes a list and passes it to
foldl, another function that also takes a list. An alternative, and cleaner implementation uses partial function application.
By partially applying
foldl, we created a new function that takes a list of numbers and returns their product. This is exactly the same signature as our
prod function. Idiomatic Haskell uses a pointfree style.
Haskell even allows partial function application at the type level. In Haskell, all values have types. Types also have types. The type of a type is called a kind.
“hello” is a value of type
[Char], a list of characters. The kind of
*. This means that it’s a concrete type; a type you can create a value of.
Haskell supports parameterized types. These are similar to templates in C++, Java, and Scala. Parameterized types don’t specify the type of the values they work with. For example, in Haskell, lists are a parameterized type. All of the list functions are generic enough to work with any type of list, for example, a list of numbers
[1, 2, 3] or a list of strings
["one", "two", "three"].
Parameterized types can take any number of type parameters. For example,
Either a b is a parameterized type used to represent success or failure. It contains two type parameters
a represents the failure value and
b represents the success value. A parameterized type can be partially applied by supplying less than its total number of type parameters.
First we show that
Either is a type that takes a concrete type,
*, and returns a type that takes a concrete type and returns a concrete type. Partially applying
String returns a type that takes a concrete type and returns a concrete type. Finally, fully applying
Either to two concrete types, results in a concrete type.
Partial function application is often considered a design pattern in mainstream programming languages and often implemented using external libraries or special syntax. It’s a good example of how a design pattern is often a workaround of a restriction in an inadequate language. In Haskell, partial function application is ubiquitous because of its low ceremony and intuitive syntax.