The F# pipe-forward can be expressed as:
let (|>) x f = f x
For example:
let SimpleFunction (a : typeA) (b : typeB) (c : typeC)=
printf "OK."
// No problem here.
SimpleFunction a b c
// Using pipe-forward
c |> SimpleFunction a b
// No problem here. Interpreted as the same as above.
However, according to the documentation, the pipe-forward operator is left-associative.
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/
So, I expected the pipe-forward statement:
// Original Expression
c |> SimpleFunction a b
// to be equivalent to:
(c |> SimpleFunction) a b
// Which is equivalent to:
SimpleFunction c a b
// Error!! SimpleFunction takes typeA, then typeB, then typeC.
Why does the compiler not "interpret" the pipe-forward expression as the error expression? Do I have any confusion about the operator precedence/associativity?
Additional Sources:
http://theburningmonk.com/2011/09/fsharp-pipe-forward-and-pipe-backward/
What is associativity of operators and why is it important?
https://en.wikipedia.org/wiki/Operator_associativity
The associativitity of a binary operator only matters when you have two or more occurrences of the same operator. When you have different operators (here: |> and juxtaposition), what matters is their relative precedence.
Juxtaposition has a higher precedence than |>, therefore
c |> SimpleFunction a b
is parsed like
(c) |> (SimpleFunction a b)
so, by the definition of |>, it's equivalent to
(SimpleFunction a b) (c)
which would usually be written
SimpleFunction a b c
That last equivalence is due to juxtaposition being left-associative.
The fact that |> is left-associative means that an expression like
x |> f |> g
is parsed as
(x |> f) |> g
which is equivalent to
g (f x)
i.e. chains of |> express function composition — successive pipeline steps — and not passing more arguments to a function.
Functions are curried by default, you original expression is actually like this:
c |> (SimpleFunction a b) which then becomes (SimpleFunction a b) c. For this to work function application will have to have precedence.
Related
I am learning F# and the use cases of the |>, >>, and << operators confuse me. I get that everything if statements, functions, etc. act like variables but how do these work?
Usually we (community) say the Pipe Operator |> is just a way, to write the last argument of a function before the function call. For example
f x y
can be written
y |> f x
but for correctness, this is not true. It just pass the next argument to a function. So you could even write.
y |> (x |> f)
All of this, and all other kind of operators works, because in F# all functions are curried by default. This means, there exists only functions with one argument. Functions with many arguments, are implemented that a functions return another function.
You could also write
(f x) y
for example. The function f is a function that takes x as argument and returns another function. This then gets y passed as an argument.
This process is automatically done by the language. So if you write
let f x y z = x + y + z
it is the same as:
let f = fun x -> fun y -> fun z -> x + y + z
Currying is by the way the reason why parenthesis in a ML-like language are not enforced compared to a LISP like language. Otherwise you would have needded to write:
(((f 1) 2) 3)
to execute a function f with three arguments.
The pipe operator itself is just another function, it is defined as
let (|>) x f = f x
It takes a value x as its first argument. And a function f as its second argument. Because operators a written "infix" (this means between two operands) instead of "prefix" (before arguments, the normal way), this means its left argument to the operator is the first argument.
In my opinion, |> is used too much by most F# people. It makes sense to use piping if you have a chain of operations, one after another. Typically for example if you have multiple list operations.
Let's say, you want to square all numbers in a list and then filter only the even ones. Without piping you would write.
List.filter isEven (List.map square [1..10])
Here the second argument to List.filter is a list that is returned by List.map. You can also write it as
List.map square [1..10]
|> List.filter isEven
Piping is Function application, this means, you will execute/run a function, so it computes and returns a value as its result.
In the above example List.map is first executed, and the result is passed to List.filter. That's true with piping and without piping. But sometimes, you want to create another function, instead of executing/running a function. Let's say you want to create a function, from the above. The two versions you could write are
let evenSquares xs = List.filter isEven (List.map square xs)
let evenSquares xs = List.map square xs |> List.filter isEven
You could also write it as function composition.
let evenSquares = List.filter isEven << List.map square
let evenSquares = List.map square >> List.filter isEven
The << operator resembles function composition in the "normal" way, how you would write a function with parenthesis. And >> is the "backwards" compositon, how it would be written with |>.
The F# documentation writes it the other way, what is backward and forward. But i think the F# language creators are wrong.
The function composition operators are defined as:
let (<<) f g x = f (g x)
let (>>) f g x = g (f x)
As you see, the operator has technically three arguments. But remember currying. When you write f << g, then the result is another functions, that expects the last argument x. Passing less arguments then needed is also often called Partial Application.
Function composition is less often used in F#, because the compiler sometimes have problems with type inference if the function arguments are generic.
Theoretically you could write a program without ever defining a variable, just through function composition. This is also named Point-Free style.
I would not recommend it, it often makes code harder to read and/or understand. But it is sometimes used if you want to pass a function to another
Higher-Order function. This means, a functions that take another function as an argument. Like List.map, List.filter and so on.
Pipes and composition operators have simple definition but are difficult to grasp. But once we have understand them, they are super useful and we miss them when we get back to C#.
Here some explanations but you get the best feedbacks from your own experiments. Have fun!
Pipe right operator |>
val |> fn ≡ fn val
Utility:
Building a pipeline, to chain calls to functions: x |> f |> g ≡ g (f x).
Easier to read: just follow the data flow
No intermediary variables
Natural language in english: Subject Verb.
It's regular in object-oriented code : myObject.do()
In F#, the "subject" is usually the last parameter: List.map f list. Using |>, we get back the natural "Subject Verb" order: list |> List.map f
Final benefit but not the least: help type inference:
let items = ["a"; "bb"; "ccc"]
let longestKo = List.maxBy (fun x -> x.Length) items // ❌ Error FS0072
// ~~~~~~~~
let longest = items |> List.maxBy (fun x -> x.Length) // ✅ return "ccc"
Pipe left operator <|
fn <| expression ≡ fn (expression)
Less used than |>
✅ Small benefit: avoiding parentheses
❌ Major drawback: inverse of the english natural "left to right" reading order and inverse of execution order (because of left-associativity)
printf "%i" 1+2 // 💥 Error
printf "%i" (1+2) // With parentheses
printf "%i" <| 1+2 // With pipe left
What about this kind of expression: x |> fn <| y ❓
In theory, allow using fn in infix position, equivalent of fn x y
In practice, it can be very confusing for some readers not used to it.
👉 It's probably better to avoid using <|
Forward composition operator >>
Binary operator placed between 2 functions:
f >> g ≡ fun x -> g (f x) ≡ fun x -> x |> f |> g
Result of the 1st function is used as argument for the 2nd function
→ types must match: f: 'T -> 'U and g: 'U -> 'V → f >> g :'T -> 'V
let add1 x = x + 1
let times2 x = x * 2
let add1Times2 x = times2(add1 x) // 😕 Style explicit but heavy
let add1Times2' = add1 >> times2 // 👍 Style concise
Backward composition operator <<
f >> g ≡ g << f
Less used than >>, except to get terms in english order:
let even x = x % 2 = 0
// even not 😕
let odd x = x |> even |> not
// "not even" is easier to read 👍
let odd = not << even
☝ Note: << is the mathematical function composition ∘: g ∘ f ≡ fun x -> g (f x) ≡ g << f.
It's confusing in F# because it's >> that is usually called the "composition operator" ("forward" being usually omitted).
On the other hand, the symbols used for these operators are super useful to remember the order of execution of the functions: f >> g means apply f then apply g. Even if argument is implicit, we get the data flow direction:
>> : from left to right → f >> g ≡ fun x -> x |> f |> g
<< : from right to left → f << g ≡ fun x -> f <| (g <| x)
(Edited after good advices from David)
I am well-versed in using the >> and << operators in F#. However, after looking in the F# source to establish a deeper understanding I became confused with this:
let inline (>>) f g x = g(f x)
let inline (<<) f g x = f(g x)
How do I interpret these expressions conceptually? Also, how would you describe these expressions? Are they defining a type?
I think the best way to describe it is with an example, as looking at the definition can be a little confusing. Let's say you had this:
let isEven x = x % 2 = 0
[1 .. 99] |> List.filter (fun x -> not (isEven x))
Using the composition operators you could rewrite it as one of these instead:
[1 .. 99] |> List.filter (isEven >> not)
[1 .. 99] |> List.filter (not << isEven)
More genericly, if you have something like this:
data |> a |> b |> c
You can rewrite it like this:
data |> (a >> b >> c)
and interpret a >> b >> c as do a, then do b, then do c. If you prefer the more traditional backwards ordering:
(a (b (c data)))
you can rewrite it as
((a << b << c) data)
This is also called the point free style. In normal cases it can be harder to read than using the normal style, but when passing to higher-order functions, it can be easier to read as you avoid having to add the (fun x -> ) noise.
As the msdn page for F# functions says, "Functions in F# can be composed from other functions. The composition of two functions function1 and function2 is another function that represents the application of function1 followed the application of function2."
It can be thought of as similar to the pipe operators, just without specifying the last/deepest parameter; e.g. the following two lines are equivalent:
let composed = f >> g
let piped x = g <| f x
Also see this question for more information.
Given a string of digits, I would like to have a sequence of tuples mapping the non-zero characters with their position in the string. Example:
IN: "000140201"
OUT: { (3, '1'); (4, '4'); (6, '2'); (8, '1') }
Solution:
let tuples = source
|> Seq.mapi (fun i -> fun c -> (i, c))
|> Seq.filter (snd >> (<>) '0')
It seems like (fun i -> fun c -> (i, c)) is a lot more typing than it should be for such a simple and presumably common operation. It's easy to declare the necessary function:
let makeTuple a b = (a, b)
let tuples2 = source
|> Seq.mapi makeTuple
|> Seq.filter (snd >> (<>) '0')
But it seems to me that if the library provides the snd function, it should also provide the makeTuple function (and probably with a shorter name), or at least it should be relatively easy to compose. I couldn't find it; am I missing something? I tried to build something with the framework's Tuple.Create, but I couldn't figure out how to get anything other than the single-argument overload.
But it seems to me that if the library provides the snd function, it should also provide the makeTuple function.
F# assumes that you decompose tuples (using fst, snd) much more often than composing them. Functional library design often follows minimal principle. Just provide functions for common use cases, other functions should be easy to define.
I couldn't find it; am I missing something?
No, you aren't. It's the same reason that FSharpPlus has defined tuple2, tuple3, etc. Here are utility functions straight from Operators:
/// Creates a pair
let inline tuple2 a b = a,b
/// Creates a 3-tuple
let inline tuple3 a b c = a,b,c
/// Creates a 4-tuple
let inline tuple4 a b c d = a,b,c,d
/// Creates a 5-tuple
let inline tuple5 a b c d e = a,b,c,d,e
/// Creates a 6-tuple
let inline tuple6 a b c d e f = a,b,c,d,e,f
I tried to build something with the framework's Tuple.Create, but I couldn't figure out how to get anything other than the single-argument overload.
F# compiler hides properties of System.Tuple<'T1, 'T2> to enforce pattern matching idiom on tuples. See Extension methods for F# tuples for more details.
That said, point-free style is not always recommended in F#. If you like point-free, you have to do a bit of heavy lifting yourself.
The #pad's answer is great, just to add my 2 cents: I am using similar operator
let inline (-&-) a b = (a, b)
and it looks very convenient to write let x = a -&- b
Maybe you'll find this operator useful too
In F#, is there a way to treat an operator as a function? In context, I'd like to partially apply operators (both infix and prefix), but the compiler only seems happy to partially apply functions.
Example:
Instead of being able to write List.map (2 **) [0..7];; I have to define my own function pow x y (and then another function let swap f x y = f y x;; because the compiler won't let me partially apply |> either, as in List.map (|> pow 2) [0..7];;.) In the end, my code needs to be List.map (swap pow 2) [0..7];; to work.
I would just do:
[0..7] |> List.map (fun n -> pown n 2)
Or, if 2 is the base:
[0..7] |> List.map (pown 2)
This works too:
[0.0..7.0] |> List.map (( ** ) 2.0)
There are no 'operator sections' a la Haskell; use a lambda to partially apply operators, e.g.
(fun x -> x - 10)
You can partially apply the first argument if you make an infix operator be prefix by surrounding it in parens, e.g.
(fun x -> 10.0 / x)
and
((/) 10.0)
mean the same thing.
Tacit or point-free style programming allows one to create functions without regard to their arguments. Can this be done in F#?
Just to go with Chuck's answer & Chris Smiths' comment, you could write
let digits = string_of_int >> String.length
digits 9000;; // 4
[1; 10; 100] |> List.map digits;; // [1;2;3]
When you combine those composition & pipeline operators with higher-order functions, you can do complicated stuff very succinctly:
let prodSqrtAbs = Seq.map (abs>>sqrt) >> Seq.reduce (*)
prodSqrtAbs [| -9.0; 4.0 |];; // 6.0
EDIT: I just read about J and its implicit fork operator. That is very powerful. You can build equivalent higher-order operators in F#, but they won't be applied implicitly. So, for example, first define lift (using explicit arguments)
let lift op a b x = op (a x) (b x)
and then apply it explicitly
let avg = lift (/) List.sum List.length
to get something resembling the J example on the Wikipedia page you linked to. But its not quite "tacit."
Sure. All you need is function composition and currying, and both of these are possible in F#.
let compose f1 f2 = fun x -> f1 (f2 x);;
let digits = compose String.length string_of_int;;
digits 9000;; // 4