Railway Oriented Programming In Elixir

A while back Scott Wlaschin posted a great blog post on what he called “Railway Oriented Programming.”  It’s a great treatment of a common problem in adopting functional programming; how to handle a chain of functions when one of the functions in the chain may return an error.  Of course stopping the chain when the first error is returned isn’t a difficult issue; the difficulty arises in structuring the code such that the code can continue running all the functions until and unless an error is encountered.  It’s a great read and a powerful idea.

So today someone asked a question on StackOverflow about how to accomplish basically that same idea in Elixir.  A little tricky to translate Scott’s ideas to Elixir because in his article his code is specified in F# and he created a special type to capture the success or failure of a function. F# is statically typed; elixir is dynamically typed which is, of course, a substantial difference.

At any rate, I saw the question and didn’t see that Saša Jurić had already posted a great answer.  So I posted my answer and then noticed his.  I mention this because I want to be clear; I wasn’t trying to steal anyone’s thunder or trying to enhance my rep or anything like that.  I just got excited; “hey, I know the answer to that question!” so I just rushed off and hacked together some demonstration code without bothering to look any further.  I am pleased to see that the answer I came up with was pretty much the same as Saša’s; I consider him an extremely smart developer and coming up with an answer pretty similar to his  independently makes me feel a bit more confident of my own skills.

The TL;DR version is that you need to construct the functions to be applied in such a way that you can carry forward an indication of either success or failure along with the value to be checked.

defmodule Password do

  defp password_long_enough?({:ok = _atom, p}) do
    if(String.length(p) > 6) do
      {:ok, p}
    else
      {:error,p}
    end
  end

  defp starts_with_letter?({:ok = _atom, p}) do
   if(String.printable?(String.first(p))) do
     {:ok, p}
   else
     {:error,p}
   end      
  end


  def password_valid?(p) do
    {:ok, _} = password_long_enough?({:ok,p}) 
    |> starts_with_letter?
  end

end

And one would use it like so:

iex(7)> Password.password_valid?("ties")
** (FunctionClauseError) no function clause matching in Password.starts_with_letter?/1
    so_test.exs:11: Password.starts_with_letter?({:error, "ties"})
    so_test.exs:21: Password.password_valid?/1
iex(7)> Password.password_valid?("tiesandsixletters")
{:ok, "tiesandsixletters"}
iex(8)> Password.password_valid?("\x{0000}tiesandsixletters")
** (MatchError) no match of right hand side value: {:error, <<0, 116, 105, 101, 115, 97, 110, 100, 115, 105, 120, 108, 101, 116, 116, 101, 114, 115>>}
 so_test.exs:21: Password.password_valid?/1
iex(8)>

A couple of comments on the code.  Someone might ask why password_long_enough?  gets a parameter of a tuple of atom and string. Since it’s the first function in the chain it’s not really needed; all it needs is the string.  I constructed in this way for a few reasons:

  1. It’s more parallel to the other function
  2. The functions are really intended to be rules which should be checked so there should not be an order dependency. That is, they should be interchangeable.

Basically by adding any predicate function that we wish given that it takes a tuple of an atom and a string and the atom is matched against :ok, we can add any arbitrary set of tests we wish to add.

As with most things on this blog, I write this down as much for my own reference as for the edification of others who might read it.  I’m pretty sure at some point in the future, I will need to validate a value through a set of predicate functions and I’ll look at this blog post to see how I can do it.


EDIT: I was reminded of a great blog post written up by Zohaib Rauf on precisely this same idea.  Well worth reading.

11 responses

  1. I recently watched Dave’s keynote from last years elixir conf ( https://www.youtube.com/watch?v=5hDVftaPQwY ), so as an exercise I wrote a version of this that eliminates the if statements and only contains one line functions. It also eliminates the need to pass two variables. In one version (password_valid?), it does this by returning either the original string or :invalid from each test in the pipeline. Each test also uses a function pattern match to short-circuit and return :invalid if it is passed :invalid. The other version (password_valid!) accomplishes this by simply raising an error when an error state is detected.

    https://github.com/TheFirstAvenger/elixir-password_checker

  2. Wow. Thank you so much. This is exactly why I blog; to hear other points of view. That’s a great approach you’ve developed there; it’s certainly something I’ll add to my Elixir idioms.

    • Yeah the whole “almost all one liners” approach is interesting, I haven’t figured out yet where I will end up on the scale of using them, even Dave mentions it’s not really for everyone, but it does get you thinking different about elixir when you shoot for it (highly recommend watching it if you haven’t already). It also took me a while to eliminate if/else statements, as I would cringe at converting it to:

      case (something) do
      true -> a
      false ->b
      end

      but once I realized that (something) usually can be broken down and made part of the case instead of passing a boolean to the case, it became much better. For example:

      if(String.length(p) > 6) do
      {:ok, p}
      else
      {:error,p}
      end

      becomes

      case String.length(p) do
      a when a > 6 -> {:ok, p}
      _ -> {:error, p}
      end

      • I think some of that is down to personal preference. There’s also an element of readability–least for me. I have a little harder time reading the more terse expressions.

  3. Onorio, I love the article. Just a very nice explanation of the reasons for all of your code, which I generally don’t take the time to elucidate in my own – seeing someone do it makes it clear that it would be valuable for me to make fewer assumptions about people understanding why I do something a certain way 🙂

    Here’s a fun addition for you – have you considered adding a typespec (type named maybe, perhaps) that outlines the valid sorts of responses to these functions? Then you can just say “any railway needs to be composed of Maybe functions.” Any dialyzer exercises are fun 🙂

  4. Pingback: ruby2elixir/rop – GITROOM

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: