Herein I will describe my programming style as well as my experience with Haskell, as an example for purely functional programming.
I sometimes call my style of programming a-to-b programming. To make this whole thing more practical I will just add a few examples written in Haskell.
What do I mean by a-to-b? Every single application I know is basically just performing data transformation. You have some data a of which you make some data b.
This does not hold true for IO, which includes:
It does however hold true for applications which you might not expect to be designed that way:
([DataBaseRow],Request) -> ([DataBaseModification],Response)
([CliParameter],StdIn) -> (ExitCode,StdOut)
So how does all of that relate to functional programming (and Haskell)? Imagine a complete web application which is structured into smaller parts each doing one single thing (adhering to the UNIX philosophy). One of these modules could be something along the lines:
-- called with a "map" from path to image and the request
-- returns the response to be sent
handleRequest :: [(Path,Image)] -> Request -> Response
handleRequest images request =
makeResponse
(
resizeImage
(getDimension request)
(lookup (getPath request) images)
)
-- creates a response from an image (add headers and so on)
makeResponse :: Image -> Response
-- resize the image somehow
resizeImage :: Dimension -> Image -> Image
-- get the requested dimension from the request
getDimension :: Request -> Dimension
Dear Haskellers, please excuse me for leaving out the Maybe
part of the lookup
function. It's for readability.
Dear Non-Haskellers, believe me if I tell you that Haskell's type system kind of pushes you into the right direction.
These benefits are applicable to both Functional Programming and the a-to-b model:
No state-keeping within your logic. Have a look at the handleRequest
function above. It does not keep state at all. If anything was to keep state it might just get one state as an argument and return the new state. The state-keeping would then be abstracted from your application. If talking about web applications this could be your session, stored in memcached transparently. Combined with Haskell's laziness you'd probably end up not even requesting unneeded state.
There will be no race conditions in this code. You will be able to quickly scale out with more threads or even more hosts if needed. You get all this "for free".
Oh noes, you found a bug. Good luck reproducing this without full step-by-step instructions. With functional programming you simply need to log your state and request as soon as an error occurs and you have everything you need to reproduce it. Debugging will be ridiculously easy.
If you have a high unit-test coverage then you have actually tested everything that could go wrong because there are no side-effects in pure functions.
You can easily replace a function that does not perform as it should and, once it works, it probably keeps on working forever.
If you profile your application you see how long each function takes and how often it's called. This makes it easy to find a bad performing function and simply rewrite it, if possible, or reduce it's usage or the way it's used.
Adding a feature is as easy as adding the new functions and putting a call to them somewhere. You cannot break any of the existing parts of the program if you do not touch them due to the complete lack of side-effects.
I might at some point update this post, but over at my own blog.
This is how I structure basically everything. Side effects on far edges, pure logic in the middle. It's only when I break from this pattern that I regret the code I write. I use Elixir for my web applications. The only thing I miss is the type system
What framework do you use for your "generic web applications"?
The chapter "Application" (the one mentioning generic web applications) was meant as describing what services do from an outside perspective.
Internals may very well vary, but emphasizing on this structure removes a few problems (mostly state) and enables some performance optimizations (said laziness for external data).
To be honest, I did not yet write any meaningful application.
Mostly reinventing the wheel for educational purposes.
Haskell is just my hobby, as opposed to my job which is a GNU/Linux sysadmin.
The sysadmin background kind of taught me to just take already existing components and stick them together.
In sysadmin world this means sticking tools together with shell-scripts.
In Haskell world this means taking libraries like async or conduit and libraries for parsing the application specific protocols (HTTP, JSON) and sticking them together which, due to purely functional nature of those libraries is actually pretty easy.
I also mostly use Haskell itself for parsing (Parsec), so there is little I know in that respect.
However once again my sysadmin background helps out here: CLI-tools mostly take a stream of input on stdin and map it to something else on stdout.
If you write your "business logic" in a way that it takes a continuous stream of events and responds to each one using a response and a state-change then you can use it in any environment you like, as long as you can map something (HTTP requests, UDP packets, messages on unix-sockets, Websockets messages,.…) to that event stream.
Do not hesitate to ask for clarification, I'm pretty lousy in communicating my thoughts.
Any insights on how to practically use Haskell on the web? Is it possible? What areas have typical use cases for Haskell?