Originally, this blogpost was titled “Use apply and carry on”, but I guess naming the function apply
after Kotlin (damn you, Kotlin!) wasn’t the most preferred option by the public. I did like apple |> apply eat
semantics but if Scott Wlaschin says apply
is a different thing then it is a different thing. To be precise Scott’s is apply: (a -> b) -> a -> b
and mine was (emphasize on was) apply: (a -> unit) -> a -> a
. Apparently, the function which does whatever I wanted apply
to do is called tee
(after Unix command) or tap
(at least in Ruby and Ramda.js).
So I’ve edited it and replaced apply
with tap
, but essentially it is almost unchanged… Let’s go then.
One of everyone’s favourite features of F# is pipe (|>
) operator. It allows to pipe output of one function as input to another function preserving visual honesty. The general idea is that, in English, we read left-to-right and top-down. In C# (C, C++, Java, Pascal, Python) we read in all possible directions, most likely top-down for overall structure but botton-up and right-to-left for single statements.
For example:
1 | var y = Math.Round(Math.Exp(Math.Sin(x*5.2 + 7.1))); |
starts at the right end going left-to-right for a moment (x*5.2 + 7.1
) but then turns right-to-left with Math.Sin
, Math.Exp
and finally Math.Round
(in this order). In F# the pipe operator (|>
) allows to write code exactly in the same order as it is going to be executed:
1 | let y = x*5.2 + 7.1 |> Math.Sin |> Math.Exp |> Math.Round |
Around the world, many hours have been spent arranging function arguments (guilty!) to allow such seamless experience. But sometimes, the universe is against us. Let’s assume we would like to print out the value after Math.Sin
. The conservative approach would be quite intrusive - we would need to break expression in half:
1 | let temp = x*5.2 + 7.1 |> Math.Sin |
Whoa! That is intrusive.
But here comes the rescue. The tap
function implemented as:
1 | let tap func arg = func arg; arg |
The function itself is trivial, it takes a function and an argument, executes given function with given argument but then returns it, so the argument goes through the function:
1 | let y = x*5.2 + 7.1 |> Math.Sin |> tap (printf "%g") |> Math.Exp |> Math.Round |
In the example above, the value passed between Math.Sin
and Math.Exp
has been redirected “for a moment” to printf "%g"
without any temporary variables or breaking the flow.
Some say, you tap into the computation stream, maybe that’s a good way to think about it.
Recently I needed to shuffle an array. The algorithm I used shuffles array in place:
1 | let inline swapInPlace i j (array: 'a[]) = |
(Random.randomInt
is not a standard function, but its implementation is irrelevant for this example)
I needed it as pure function, which will not mutate input array, just return shuffled version of it. Let’s do it:
1 | let shuffle array = |
Maybe we can do better with tap
? Yes, we can:
1 | let shuffle array = array |> Array.copy |> tap shuffleInPlace |
Much better.
So, use tap and carry on!