My Way of (Purely Functional) Programming
#benaryorg Mar 15 '17
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:
- storage applications
- databases (PostgreSQL)
- caches (varnish)
- key-value stores (memcached)
- one-way communication
It does however hold true for applications which you might not expect to be designed that way:
- generic web applications:
([DataBaseRow],Request) -> ([DataBaseModification],Response)
- command line applications:
([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.