Learning F # with style
When passing a function to another function that needs to compute the result and then pass that value as an argument to another function, I find that I can "compose" the following code in 5 different ways.
let testnum testFun =
testFun 4
printfn "result: %b" (testnum (=) 0)
printfn "result: %b" <| testnum (<) 0
printfn "result: %b" (testnum <| (>=) 0)
testnum <| (=) 0 |> printfn "result: %b"
printfn "resutl: %b" << testnum <| (<>) 0
I like the style without parentheses better, but now I'm wondering if there is a preferred style that assumes my goal is readability and maintainability of my code?
source to share
Prefers pipe |>
, this help with pinouts and readability
(=) 3 |> testnum |> printfn "result: %b"
you can easily split it into multiple lines
(=) 3
|> testnum
|> printfn "result: %b"
If there are few arguments, you can pass them directly
testnum (=) 4 |> printfn "result: %b"
but use a pipe |>
or composition >>
as g x |> f
or x |> g |> f
instead of a nested function call f(g(x))
, more idiomatic. It also helps with the output, for example instead of the operator.
open System.Linq
let double x = x * 2
let double1 items = items |> Seq.map double
[1;2;3] |> double1 |> printfn "%A"
//this doesn't compile,
//error: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed ..
//
// let double2 items = items.Select(double)
// [1;2;3] |> double2 |> printfn "%A"
let double3 (items:seq<_>) = items.Select(double)
[1;2;3] |> double3 |> printfn "%A"
example: https://dotnetfiddle.net/K0u3NQ
source to share
For your example, I would choose the first option:
printfn "result: %b" (testnum (=) 0)
The second is passable:
printfn "result: %b" <| testnum (<) 0
Others are too far-fetched, they look like an exercise in obfuscation.
I only use the "inverse" pipe operator in two situations:
-
when I have a constructor of a type that would otherwise need nested parentheses, so
Some <| Foo ("bar", "baz")
instead ofSome (Foo ("bar", "baz"))
-
when I want to pass one anonymous function as the last argument - it tells perfectly where the action is:
lock sync <| fun () -> ...
For what it's worth, if there are multiple anonymous functions passed as arguments, I would normally put each of them in parentheses (a notable exception is when one of them contains multiple characters long one liner and the other multiple lines) then I I'll go with the above syntax anyway).
As far as pipelining goes, I would usually go with |>
for longer pipelines and use function construct >>
for shorter ones, when I no longer need to reference the argument in the function body.
I do not think I ever put |>
and <|
in a single line without brackets one of them. It looks strange.
source to share