Thursday March 31st 2016

Edition 004

The Power of Partial Application – Part 2

by Scott Wlaschin

FP Bridge Consulting

Some functional programming principles can seem bizarre and hard to understand when you first encounter them, but are immensely powerful when you master them. In the final part of this two part article, Scott will look at two more examples of the technique called “partial application” to show you how it is used in practice. You can read the first part here

Partial application for dependency injection

In object-oriented design, the concept of “dependency injection”
is used to parameterize one object with another, typically by
passing in an interface or service in the constructor.

In functional programming, there are no classes or objects, but
partial application can act as the equivalent technique.

For example, let’s say that you need a function that looks up a
customer in a database.

The function will take a customer id as input and return a
Customer value, so its signature will
look like this:

type GetCustomer = CustomerId -> Customer

But where’s the database? How can this function work without a
database to talk to?

The answer is that we can define an implementation with an extra
parameter for the database connection. And in the implementation,
we can use the passed-in connection to do the work, like this:

let getCustomerFromDatabase connection customerId =
// from connection
// select customer
// where customerId = customerId

But we can now do a trick! We can pass in only the first
parameter (the connection), and get back a new function which now
just takes the customerId, exactly as we want.

let getCustomer =
getCustomerFromDatabase myConnection

What’s more, we can easily mock a different version of the same
function for testing. For example, we could use an in-memory
map/dictionary as a lookup. The implementation of that function
would look like this:

let getCustomerFromMemory map customerId =
map |> Map.find customerId

But again, if we pass only the map, we can get back a new
function that looks just like any other “getCustomer” function.

let getCustomer =
getCustomerFromMemory myMap

So now we have two versions of a “getCustomer” function. One was
created by partially applying the database connection, and one
was created by partially applying the map. They both have the
same signature and are completely interchangeable, and they have
both been “injected” with a different dependency.

I hope you can see that this approach gives you exactly the same
benefits that dependency injection and mocking do in the
object-oriented world.

Partial application in conjunction with validation

Finally, let’s look at one more use of
partial application, which is to gradually build up a constructor
function that can handle bad input.

Say that you have a “Person” type that is built from other
types: Name, Age and Email.

type Name = Name of string
type Age = Age of int
type Email = Email of string

type Person = {
name: Name
age: Age
email: Email }

Now let’s also say that the Name, Age and Email types have to be
valid before they can be created.

For example, the name must not be blank, the age must be a
positive number, and the email must contain an “@” sign.

We can enforce this by having constructors that do validation,
and which return an optional value: “Some” if the input is valid,
but “None” if the input is not valid. Here’s some code that
demonstrates this:

open System.Text.RegularExpressions
open System

// constructor that might return a Name
// val name : s:string -> Name option
let name s =
if String.IsNullOrWhiteSpace(s)
then None
else Some (Name s)

// constructor that might return an Age
// val age : i:int -> Age option
let age i =
if i <= 0 then None
else Some (Age i)

// constructor that might return an Email
// val email : s:string -> Email option
let email s =
if Regex.IsMatch(s,@"^\S+@\S+\.\S+$")
then Some (Email s)
else None

Here’s the problem: we want to create a valid Person, but the
various inputs might be valid or not. Only if all the inputs are
valid should we create the Person. How can we do this?

The imperative approach might be to do all the validations, and
then if they all valid, then create a new Person with the valid
values. Here’s an example of how this might look:

// create a new Person
let person unsafeName unsafeAge unsafeEmail =
// validate each input
let name = name unsafeName
let age = age unsafeAge
let email = email unsafeEmail

if name.IsSome && age.IsSome && email.IsSome
// if they are all valid
then Some {name=name.Value; age=age.Value;
email=email.Value}
// else return None
else None

Unfortunately, this code is ugly and error prone and it will only
get smellier as more parameters are added.

It would be nice if we could build the Person incrementally,
processing one input at a time, and abandoning it immediately if
we find a bad input. But the Person type is immutable and must be
built all-or-nothing, so we seem to be stuck.

However, there is a way to work incrementally, and the secret is
to use partial application.

First, we create a function that creates the Person type in the
normal way. It will assume all the inputs are valid, so it is
very simple.

let person name age email =
{name=name; age=age; email=email}

Because it is a function, we can build it up incrementally,
processing one parameter at a time.

First we pass in the first parameter, creating a partially
applied function. If the first parameter is successful, then the
partially applied function will be too (e.g. Something). But if
the first parameter is not successful, then the partially applied
function will also be unsuccessful (e.g. None)

let partial1 =
person (process) (name "Alice")

Next, we take the partially applied function (“partial1”) and
pass in the next parameter. This time the result will be
successful only if both the “partial1” and the parameter are
successful, otherwise the result (“partial2”) will be
unsuccessful.

let partial2 =
partial1 (process) (age 30)

Finally, we process the last parameter. Again the result will be
successful only if both the function (“partial2”) and the
parameter are successful, otherwise the result will be
unsuccessful.

let result =
partial2 (process) (email "a@example.com")

So finally we have our result, which will be either success or
failure.

Here’s the real code, with some helper functions “<!>” and “<*>”
defined.

let (<!>) = Option.map
let (<*>) f x =
match (f,x) with
| (Some f'),(Some x') -> Some (f' x')
| _ -> None

let partial1 = person <!> name "Alice"
let partial2 = partial1 <*> age 30
let result = partial2 <*> email "a@example.com"

This is a bit ugly, but we can remove all the intermediate steps
(partials) and put all the code together, like this:

let result = person <!> name "Alice"
<*> age 30
<*> email "a@example.com"

In this case, all the inputs are valid, and the result is just as
we would expect:

// Person option =
// Some {name=Name "Alice";age=Age 30;
// email=Email "a@example.com";}

We can also check that the error path works, by passing in
invalid parameters:

// bad name
let result = person <^> name ""
<*> age 30
<*> email "a@example.com"
// => Person option = None

// bad age
let result = person <!> name "Alice"
<*> age -30
<*> email "a@example.com"
// => Person option = None

In both these cases, we get the result “None”, as we would
expect.

So we now have an elegant way of building an immutable type, with
validation included for all parameters. Thank you, partial
application!

Summary

In this article we’ve looked at partial application and some of
its common uses. I hope that this technique has been demystified
for you somewhat, and that you can see that, far from being a
trivial trick, it is the foundation for many powerful functional
programming techniques.

Would you like to learn more?? Check out all F# courses here!

Share This Article:

The F# Gazette

Relax and enjoy a curated selection of the best F# content straight to your inbox.

Including:
  • F# news round-up
  • Interviews with industry professionals
  • Guest articles from F# experts
  • Latest F# jobs
  • F# tools, tips, tutorials and more.