In today’s post we will take a closer look at the F# forward pipe operator represented by the following |> symbol. We already saw this operator in action in my two previous posts about Polish surnames that you can find here and here. In this session we will go over:

The definition and basic usage of the |> operator

operator An advanced example written in both C# and F#, illustrating the expressive power of |>

Back to Basics

If we search for the definition of the operator in the source code, we can find this:

/// Apply a function to a value, the value being on the left, the function on the right /// arg: The argument. /// func: The function. val inline ( |> ) : arg:'T1 -> func:('T1 -> 'U) -> 'U

The function signature can be a bit scary if you’re not familiar with F#, but in reality it is pretty straightforward. Let’s look at an example:

let add a b = a + b let res1 = add 2 3 // res1 = 5

We just defined a new add function that takes 2 operators a and b and sums them. Then we execute this function once and store the result in res1, 5 in this case. But what if we used the |> operator to achieve the same effect:

let res2 = 3 |> add 2 // res2 = 5

Or, with a bit of extra formatting:

let res3 = 3 |> add 2

What happens is that we simply take the value on the left of the operator, and we pass it (or pipe it) as the last parameter of the function on the right of the operator. And that’s all we need to know. Nothing more, nothing less.

Right now you’re probably thinking “But why, Roberto? Why!?”. That’s a very pertinent question. My answer is — and I feel this deserves the HTML <blockquote> treatment —

because it allows you to chain an arbitrary amount of function calls while keeping your code readable and elegant.

Here Be Pizzas



That was pretty simple. We need a more advanced example to illustrate the true power of the forward pipe operator: Pizzas



Let’s have a look at the following C# service that makes and delivers pizzas (I voluntarily left out the implementation details so we can focus on the topic at hand). The full code is available on this public gist.

public class PizzaRecipe {} public class PizzaOrder {} public class ColdPizza {} public class HotPizza {} public class PizzaDelivery {} public static class PizzaService { public static PizzaOrder OrderPizza(PizzaRecipe recipe, string size) { return new PizzaOrder(); } public static ColdPizza PreparePizza(PizzaOrder order) { return new ColdPizza(); } public static HotPizza CookPizza(ColdPizza pizza, int temperature) { return new HotPizza(); } public static PizzaDelivery DeliverPizza(HotPizza pizza) { return new PizzaDelivery(); } }

Now, if we wanted to use this API and implement the full pizza delivery process, we’d certainly have a few design options. The first one would be:

// The ol' good way. public PizzaDelivery BigPizzaProcess(PizzaRecipe recipe) { var order = OrderPizza(recipe, "big"); var coldPizza = PreparePizza(order); var hotPizza = CookPizza(coldPizza, 180); var delivery = DeliverPizza(hotPizza); return delivery; }

This is pretty standard, quite a lot of local variables but that does the job. There’s also this option:

// The Arabic way: you read from right to left! public PizzaDelivery BigPizzaProcess(PizzaRecipe recipe) { return DeliverPizza(CookPizza(PreparePizza(OrderPizza(recipe, "big")), 180)); }

Although I might scream a bit if I saw something like this on production! As you can see, you’d have to read it from the right to the left to get the proper order of execution.Not very convenient. Also, adding new steps to the process would quickly turn the whole thing into a mess.

Finally, you might want to write a few extension methods:

public static class PizzaExtensions { public static PizzaOrder Order(this PizzaRecipe recipe, string size) { return PizzaService.OrderPizza(recipe, size); } public static ColdPizza Prepare(this PizzaOrder order) { return PizzaService.PreparePizza(order); } public static HotPizza Cook(this ColdPizza pizza, int temperature) { return PizzaService.CookPizza(pizza, temperature); } public static PizzaDelivery Deliver(this HotPizza pizza) { return PizzaService.DeliverPizza(pizza); } }

And end up with something like this:

// The nice way! public static PizzaDelivery BigPizzaProcess(PizzaRecipe recipe) { return recipe .Order("big") .Prepare() .Cook(180) .Deliver(); }

Now we’re talking! The code is clean and it almost reads like natural English. Even someone with little or no knowledge in programming would be able to quickly guess what the code is doing. I actually conducted the experiment with my girlfriend and she understood and explained the code in less that 30 seconds. Please try this at home and let me know of the results in the comments section!

It took some extra effort to get there though. The extensions methods that we created will need to be maintained and tested too. Nothing too complex so far, but it needs to be done anyway. Let’s have a look at the F# equivalent now:

open System type PizzaRecipe() = class end type PizzaOrder() = class end type ColdPizza() = class end type HotPizza() = class end type PizzaDelivery() = class end let order (size:string) (recipe:PizzaRecipe) = PizzaOrder() let prepare (order:PizzaOrder) = ColdPizza() let cook (temperature:int) (pizza:ColdPizza) = HotPizza() let deliver (pizza:HotPizza) = PizzaDelivery() let bigPizzaProcess (pizzaRecipe:PizzaRecipe) = pizzaRecipe |> order "big" |> prepare |> cook 180 |> deliver

That’s it, really! Thanks to the Pipe operator in F#, we achieved the same nice and readable code as the C# version, but without the hassle of setting up all the additional extension methods. One could argue that the F# version falls even closer to natural English, due to the absence of the all the parentheses, brackets, dots and semicolons that are present in the C# sample.

As a result, the F# code is much more compact with only 26 lines of code versus 65 lines for the C# version. That’s 40% less code to test and maintain! What we have here is only a small example, but I let you imagine what would that mean for a large-scale application.

That’s all for today. As always, don’t hesitate to leave your comments or questions at the bottom of the page.

Cheers!