HTTP and Functional Programming

I made what some might consider a bold claim on Twitter this past Friday, that HTTP holds to the functional programming paradigm. For the purposes of demonstration, I’ll be using F# as a means of declaring function and type signatures, though you can use any language in a functional style. I’ll further use type names from Microsoft’s new Web API library. (Actually, I’ll be sticking to the System.Net.Http types, which are already generally available in the HttpClient NuGet package and will be part of .NET 4.5.)

The Basics

At the bare minimum, HTTP supports an interface in which a client submits an HttpRequestMessage and responds with an HttpResponseMessage. This contract can be represented as a function with the following signature:

HttpRequestMessage -> HttpResponseMessage

I could try to just leave it here and say, “See, it’s a function!” but that would be cheating a bit. Nevertheless, the essential contract for HTTP is a simple function. That’s at least something.

Summary

Let’s dive a bit deeper. What precisely might one mean by “functional programming paradigm”? Typically, you’ll see some variation on the following list (which is by no means exhaustive):

  • Declarative – what not how
  • Pure functions – a function with no side-effects such as I/O or mutating global state
  • First-class and higher-order functions – functions can be created and passed as parameters just like other values
  • Referential transparency – an expression can be replaced with its value without changing program behavior
  • Memoization – a performance enhancing technique made possible by referential transparency

Aligning the above items with ideas found in HTTP, we get:

  • Declarative -> HttpRequestMessage headers
  • Pure functions -> Safe, idempotent HTTP methods such GET and HEAD
  • First-class and higher-order functions -> Hypermedia controls and content negotiation
  • Referential transparency and Memoization -> Cache control mechanisms

Discussion

Declarative HTTP

This is the easiest to demonstrate. Request headers are nothing other than a means to declare what you want (e.g. Request Line) and set expectations (e.g. Accept). The entire HttpRequestMessage exists as a means of expressing intent. As a client, you have absolutely no chance of instructing the server on how to process your request. Even RPC-style calls must abide by this constraint, at least as it concerns the request message.

Pure Functions

Some may argue this point due to the prevalence of non-conforming web applications that allow side effects on safe, idempotent methods. Nevertheless, HTTP as it is defined specifies that GET and HEAD methods should be safe. This is quite important for other attributes of HTTP, such as the ability to cache representations, which I’ll discuss a bit further down.

First-class and Higher-order Functions

I will acknowledge that this is my weakest point. However, the fact that both the client and server are able to communicate “callable” options appears very close to the very mechanism used when supplying callbacks or continuations in functional programming. It’s close enough for me. You are free to disagree.

Referential Transparency and Memoization

Referential transparency, which is supported by the presence of pure functions (see above), allows us to safely support caching, known as memoization in functional programming. In functional programming languages, memoization allows us to offset the cost of immutability by calling a function once and then re-using the original value rather than continuously invoking the function over and over. It’s especially useful for expensive operations. In much the same way, HTTP provides the ability to cache representations at intermediaries to speed the process of returning a response.

What About Non-Safe-Idempotent and Non-Idempotent Methods?

Very few functional languages are considered pure, Haskell arguably the most popular. F#, OCaml, Lisp, Scala, JavaScript and others all support mutation. Truly, you would be unable to do very much without the ability to change state somewhere. Thus, these other methods are both useful and in no way against the idea that HTTP supports the functional style. Much as even Haskell can eventually persist data in mutable storage such as a database, HTTP supports controlled mutation (albeit without explicit monads; count your blessings).

Conclusion

I think the above arguments support my original idea pretty well, but I’m curious what you think. Also, I certainly am using this as a means of drawing attention to a project I’ve been working and churning on, related to both F# and Web API. In upcoming posts, I’ll introduce Frank, which is finally approaching a fairly stable api.

Advertisements