
Railway-oriented programming - bunderbunder
http://fsharpforfunandprofit.com/posts/recipe-part2/
======
tel
The elided punchline is that this entire essay is "just the `Either` monad".

If you can forgive the snark there, the point to take home is that this entire
pattern of programming can be nicely summarized through some very high-order
topics like Monads. That's one "why" for "why should I learn what 'monad'
means".

A very similar essay could be written for non-deterministic programming ("it's
'just' the List or Logic monad"), for backtracking parser combinators
("they're 'just' the State + List monad"), or for state threads themselves
("they're 'just' the State monad"). Each of these programming patterns is
neatly summarized by the "obvious" behaviors of a simple type.

One of the powers of Haskell is that it lets you abstract over patterns like
this and talk about them inside your language. That's why Haskell has such a
fetish with Monads—it's as though GoF had been formally encoded into the
language. This notion comes up a lot under the name "Monad" but it might also
be "Profunctor" or "Applicative" or "Category".

I really love this essay because it really goes through all the details.
Oftentimes an advanced Haskell/ML/F# programmer might summarize a large topic
as "it's just the X monad" anticipating that someone familiar with this kind
of programming can reverse engineer almost all of the meaning from that plain
statement. This is typically true, but certainly expects a lot of experience
from the listener.

This essay unpacks all of that meaning and presents it in easily digestible
bits. That's great technical writing.

------
gioele
I naturally tend to use this "railway" approach in my code. But often I
stumble upon various problems:

1\. The "success, but..." scenario. Sometimes you have different types of
failures to handle and this style conflates all of them into "failure". For
example, I want to WARN about something my caller; everything is OK, but there
is something to be known (The call went fine, but the file was not there so I
created it). Should information about that value be passed on the failure
pipe? If I do so I lose the ability to do simple checks like "if
failure.empty?". A third pipe for "warn"? And another one for "info"? Mhhh...

2\. There are different way to fail. There are cases in which you really
should not go on with any computation, there are cases in which can go on,
just note that you are in "danger" mode (ENOSPC).

3\. What about transient failures? Should "Network just went down" be treated
like "Server returned HTTP 404".

4\. Sometimes you want to just ignore all the errors because you are
prototyping something and you do not want a lot of boilerplate code laying
around. And definitely you do not want a catch all that hides the errors.

~~~
jerf
"1\. The "success, but..." scenario. Sometimes you have different types of
failures to handle and this style conflates all of them into "failure"."

No, it doesn't. I'm not sure where you get that, because even in this example
"Failure" carries an error message. That's a string for didactic purposes, it
can be anything you need it to be.

"2\. There are different way to fail. There are cases in which you really
should not go on with any computation, there are cases in which can go on,
just note that you are in "danger" mode (ENOSPC)."

Here is where avoiding the word "monad" probably does not play to the author's
favor. Monadic computations are able to examine the results of computations,
so nothing stops you from

    
    
         case somethingThatMayFail arg1 arg2 of
             Right x -> do
                 ... -- carry on the computation
             Left x -> -- fail out however you're going to fail
    

Which I believe also handles the rest of your questions... the pattern
conveniently propagates errors when you don't want to treat them specially,
but you can still dig in and examine them if you need to. Whatever you want to
do, you can do, because it's just a program like anything else. For 3, the
answer is "just write it the way you want it"... there isn't really an
"answer" because that's not a problem in the first place, any more than it
would be in C. And for 4, again, just do that then. Nothing's stopping you.

------
nemo1618
I'm not sure why the author goes out of his way to avoid saying "monad," but
this is certainly a monad analogy blog post whether he wants to call it that
or not.

Not that that's necessarily a bad thing; I think this article does a great job
of teaching the reader about monads without them knowing it. The subject of
the post -- error handling in a purely-functional context -- is usually my go-
to example for explaining monads.

~~~
bunderbunder
> I'm not sure why the author goes out of his way to avoid saying "monad,"

Explained here:
[http://fsharpforfunandprofit.com/about/#banned](http://fsharpforfunandprofit.com/about/#banned)

~~~
the_af
I understand why they do it, but I think they're doing themselves a
disservice. It's one thing to speak in jargon and wrongly assume your audience
will follow you:

"So this is obviously a catamorphism. This evidently a job for Kleisli
arrows!".

And a completely different thing is to construct a solution step by step,
using familiar ideas, and _then_ show that it can be seen as a monad, then
give a brief explanation of why monads are useful and what motivates them.
Which is _exactly_ what the extremely didactic _Learn you a Haskell_ site does
(seriously: look at their example for the Writer Monad. It doesn't get more
didactic and step-by-step than that)

By avoiding the correct technical terms, they are:

\- Losing precision.

\- Failing to generalize the concept.

\- Underestimating their audience's ability to understand new concepts _when
property explained_.

The last one in particular kills me. I for one do not assume Visual Basic
programmers are incapable or unwilling to learn new things.

~~~
bunderbunder
If you're familiar with the psychological phenomenon known as priming, that
may help help to explain the situation. For someone who's suffered through a
few too many many blog posts that explain monads poorly, it is possible that
simply mentioning the m-word, even in the very last sentence of the article,
may inhibit their ability to comprehend the process. Because of that, there's
a certain genius to the article's approach: Give folks who've been struggling
with monads and arrows the best possible chance to grasp the concept without
being encumbered by all the baggage that has built up around the terminology.
If they do get the concept, great, and they're virtually guaranteed to realize
shortly afterward, "Oh hey, forehead smack, those are monads!"

(Incidentally, for people who've found themselves thrust into this unfortunate
situation, a chapter titled _A Fistful of Monads_ probably isn't likely to
help much.)

To that end, I'd even go so far as to say your worries about that last bullet
point underestimate the audience's ability to understand new concepts when
properly explained much worse than the author did.

~~~
steveklabnik
> even in the very last sentence of the article,

I am not a psychologist, but isn't part of the definition of priming that it
happens before? How would saying something afterwards harm your ability to
previously understand something? Well, I guess I could see how that'd be
possible, but I'm not sure that's priming anymore.

~~~
tlarkworthy
I don't read every article top to bottom, I would waste too much time doing
that. I read the intro, and if I like, I estimate how long it will take me
with a full scroll to the bottom. I also read the bottom so I know the point
we are working up to. Sometimes the article is too long and I skip an overly
descriptive middle section.

------
sriram_malhar
Very nice explanation of monads.

sigfpe's blog has the "original" tutorial:
[http://blog.sigfpe.com/2006/08/you-could-have-invented-
monad...](http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-
and.html)

------
ciferkey
Here's the recording ([https://skillsmatter.com/skillscasts/4964-railway-
oriented-p...](https://skillsmatter.com/skillscasts/4964-railway-oriented-
programming)) of the talk that Scott Wlaschin gave at Skills Matter 2014 using
what I believe is the the same slide deck. It was probably my favorite one
from the day.

Warning though, you do have to make an account to view the video.

~~~
pi-rat
He presented the same talk at NDCOslo, available here (without registration):
[https://vimeo.com/97344498](https://vimeo.com/97344498)

Was a pretty good talk :)

------
Osmium
Reminds me a lot about 'optional chaining' in Swift (among other languages I'm
sure), at least on a superficial level. What I'm not sure about at the moment
is where best practices suggest you should use them–it seems like some
functions are written to accept optionals, while others are not, and it's not
clear why to me.

------
pygy_
I'm surprised none has mentioned promises yet, since they're an example of
this pattern applied to asynchronous code:

    
    
        getData().then(validate).then(update).then(send, handleError)
    

This reproduces the code flow of the first figure of the article.

------
NathanKP
The "railway oriented programming" is pretty much the default way of coding in
Node.js using the flow control module async. The major feature that async has
but that I don't see in this blog post would be that you can split up the
"railway" into multiple parallel paths when needed or when it is more
efficient to do so, and then join the paths back together again later in a
step which depends on the results from each of those parallel paths.

Once I started using the async.auto() it completely changed my way of thinking
about how code worked, and I've started coding in a much more functional
style. Node.js makes this very easy.

------
smoyer
I think the bypass approach is a commonly used technique when a failure in any
component exits the routine ... but it's also common for each of the failures
to be processed differently, which actually looks like the fan-out that
happens in a railway switching yard.

The author's primary example uses a "layout" I've never seen on a railway
([http://fsharpforfunandprofit.com/assets/img/Recipe_RailwaySw...](http://fsharpforfunandprofit.com/assets/img/Recipe_RailwaySwitch3.png)).
Railway switches are expensive, so you never see multiple switches going the
same direction between a pair of tracks. If there are two consecutive switches
(and going say from left to right), one shunts from track A to track B and the
other shunts from track B to track A. When space is limited, a cross-over
might be used by these are even more expensive (an provide additional gaps
where derailments can happen).

------
nercury
In imperative programming, your code can execute in two states, valid and not-
valid. You start in valid state, then any validation failure returns you to
non-valid state and you remain there. Pretty much the core of any validation
technique without bells and whistles.

valid = true

if valid and not check1(): valid = false

if valid and not check2(): valid = false

...

~~~
TheLoneWolfling
I much prefer the (equivalent) code:

if not check1(): return false

if not check2(): return false

...

------
alexatkeplar
We do a ton of railway-oriented programming at Snowplow, using the Scalaz
Validation[1]. It's a great great fit for data processing pipelines.

When we are enriching raw events, we try a few separate
validations/enrichments and then either accumulate errors or compose a valid
enriched event.

[1]
[https://gist.github.com/justjoheinz/9184859](https://gist.github.com/justjoheinz/9184859)
[2]
[https://github.com/snowplow/snowplow/blob/master/3-enrich/sc...](https://github.com/snowplow/snowplow/blob/master/3-enrich/scala-
common-
enrich/src/main/scala/com.snowplowanalytics.snowplow.enrich/common/enrichments/EnrichmentManager.scala#L295)

------
skybrian
This is quite a fancy explanation of monads. But in the end, is it worth it?

Maybe Go's somewhat verbose way of handling errors isn't so bad after all. It
seems easier than teaching your coworkers a bunch of combinators, no matter
how you try to sugar-coat it.

~~~
pekk
It's a false choice. There are ways of handling errors without so much
verbosity, and without teaching coworkers a bunch of combinators either.

------
AnimalMuppet
After thinking about it a bit, this seems to be almost exactly the behavior
that try/catch implements.

------
innguest
This is very well done. It's refreshing to see graphical explanations of
programming concepts. The analogy with tracks is a very helpful way to make
the notion of monadic contexts concrete. Great job!

I'd be curious to know how the author came up with the idea of representing
these ideas with tracks, as it is a very fortunate perspective.

~~~
mamcx
Also, the swift slides (at wwdc) show something similar

