Railway Oriented Programming
This page contains links to the slides and code from my talk “Railway Oriented Programming”.
Here’s the blurb for the talk:
Many examples in functional programming assume that you are always on the “happy path”. But to create a robust real world application you must deal with validation, logging, network and service errors, and other annoyances.
So, how do you handle all this in a clean functional way?
This talk will provide a brief introduction to this topic, using a fun and easy-to-understand railway analogy.
I am also planning to upload some posts on these topics soon. Meanwhile, please see the recipe for a functional app series, which covers similar ground.
If you want to to see some real code, I have created this project on Github that compares normal C# with F# using the ROP approach
WARNING: This is a useful approach to error handling, but please don’t take it to extremes! See my post on “Against Railway-Oriented Programming”.
I presented on this topic at NDC London 2014 (click image to view video)
Other videos of this talk are available are from NDC Oslo 2014 and Functional Programming eXchange, 2014
Slides from Functional Programming eXchange, March 14, 2014
The powerpoint slides are also available from Github. Feel free to borrow from them!
If you like my way of explaining things with pictures, take a look at my "Domain Modeling Made Functional" book! It's a friendly introduction to Domain Driven Design, modeling with types, and functional programming.
Any Haskellers reading this will immediately recognize this approach as the
specialized to use a list of a custom error type for the Left case. In Haskell, something like:
type TwoTrack a b = Either [a] (b,[a])
I’m certainly not trying to claim that I invented this approach at all (although I do lay claim to the silly analogy). So why did I not use the standard Haskell terminology?
First, this post is not trying to be a monad tutorial, but is instead focused on solving the specific problem of error handling.
Most people coming to F# are not familiar with monads. I’d rather present an approach that is visual, non-intimidating, and generally more intuitive for many people.
I am strong believer in a “begin with the concrete, and move to the abstract” pedagogical approach. In my experience, once you are familiar with this particular approach, the higher level abstractions are easier to grasp later.
Second, I would be incorrect to claim that my two-track type with bind is a monad anyway – a monad is more complicated than that, and I just didn’t want to get into the monad laws here.
Third, and most importantly,
Either is too general a concept. I wanted to present a recipe, not a tool.
For example, if I want a recipe for making a loaf of bread, saying “just use flour and an oven” is not very helpful.
And so, in the same way, if I want a recipe for handling errors, saying “just use Either with bind” is not very helpful.
So, in this approach, I’m presenting a whole series of techniques:
- Using a list of custom error types on both the left and right sides of Either (rather than, say,
Either String a).
- “bind” (
>>=) for integrating monadic functions into the pipeline.
- Kleisli composition (
>=>) for composing monadic functions.
- “map” (
fmap) for integrating non-monadic functions into the pipeline.
- “tee” for integrating unit functions into the pipeline (because F# doesn’t use the IO monad).
- Mapping from exceptions to error cases.
&&&for combining monadic functions in parallel (e.g. for validation).
- The benefits of custom error types for domain driven design.
- And obvious extensions for logging, domain events, compensating transactions, and more.
I hope you can see that this is a more comprehensive approach than “just use the Either monad”!
My goal here is to provide a template that is versatile enough to be used in almost all situations, yet constrained enough to enforce a consistent style. That is, there is basically only one way to write the code. This is extremely helpful to anyone who has to maintain the code later, as they can immediately understand how it is put together.
I’m not saying that this is the only way to do it. But I think this approach is a good start.
As an aside, even in the Haskell community there is no consistent approach to error handling, which can make things confusing for beginners. I know that there is a lot of content about the individual error handling techniques, but I’m not aware of a document that brings all these tools together in a comprehensive way.
- If you want a ready-made F# library that works with NuGet, check out the Chessie project.
- If you want to see a sample web-service using these techniques, I have created a project on GitHub.
- You can also see the ROP approach applied to FizzBuzz!
F# does not have type classes, and so you don’t really have a reusable way to do monads (although the FSharpX library
has a useful approach). This means the
Rop.fs library defines all its functions from scratch.
(In some ways though, this isolation can be helpful because there are no external dependencies at all.)
“One bind does not a monad make” – Aristotle
As I mentioned above, one reason why I stayed away from monads is that defining a monad correctly is not just a matter of implementing “bind” and “return”. It is an algebraic structure that needs to obey the monad laws (which in turn are just the monoid laws in a specific situation) and that was a path I did not want to head down in this particular talk.
However if you are interested in more detail on
Either and Kleisi composition, here are some links that might be useful:
- Monads in general.
- School of Haskell
- Real World Haskell on error handling (halfway down)
- LYAH on error handling (halfway down)
- Kleisli categories and composition
- Comprehensive error handling approaches
- Item 5 in this post
- I’m not aware of other approaches that cover all the techniques discussed in this talk. If you do know of any, ping me in the comments and I’ll update this page.