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?

+3


source to share


2 answers


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

+1


source


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.

+2


source







All Articles