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.)
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.
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 ->
- 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
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.
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
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?
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.