Make your pizzas taste better thanks to the F# Forward Pipe operator!

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
  • 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!

6 thoughts on “Make your pizzas taste better thanks to the F# Forward Pipe operator!

  1. Dawid

    Almost like pipe operator in bash 😉 Maybe that’s even the origin of that concept in F#?
    I think it’s quite normal there are such “sugars” for writing operations in F# – this is functional language, right? It must make using functions easier than OO languages. Anyway, nice stuff, making code looking much cleaner.

    Reply
    1. Youenn Post author

      You’re right, it works the same as the pipe operator in bash :). I don’t know what’s the story behind it, but I wouldn’t be surprised either if the Unix pipe was somehow mixed in it. F# as a functional-first language makes it definitely easier to create higher-order functions (that is, functions that either take functions as parameters, return a function, or even both).
      It lets you create new, custom operators very easily as well.

      Reply
  2. Pingback: F# Weekly #12, 2017 – Sergey Tihon's Blog

  3. Potom

    I like how you did it. Your articles are so easily to understand.
    Have you got any experience with other functional languages, like scala, python, r,…?
    Almost every article is comparing f# with c# (which is reasonable somehow). But I would like to know what is advantage of learning f# over other functional languages in 2017.
    Is it possible to make blog post about comparison of these languages?

    Thank you for your effort (past and future).

    Reply
    1. Youenn Post author

      Thanks for your feedback! I try to make my posts easy to read but that’s not always easy.
      Unfortunately I don’t have enough experience in the languages you listed to be able to make an honest comparison. Also, it is pretty fair and easy to compare F# with C# since both compile down to the same IL code and have the same runtime. Coming from the OO world (mainly C#) I felt like F# was designed with simplicity in mind, something that is very important to me.

      It also allows to make compromises if you need to (it is functional-first, not purely functional). Scala allows both functional and OO approaches, but I feel that it is much more powerful at abstracting things than F# is, and requires more knowledge from the developer to actually do things right.

      I’m not sure whether I will be able to write a post with a comparison between Scala and F# for example, but I can make one explaining why F# seemed like a good candidate to me, if you’re interested.

      Reply
  4. Pingback: Make your pizzas taste better thanks to the F# Forward Pipe operator | StratCom

Leave a Reply

Your email address will not be published. Required fields are marked *