12

Quite similar to this question: F# pipe first parameter

I am currently learning F# and functional programming, and I want to know if there is an easy way to pipe-forward a first argument (instead of last argument).

For example, if I want to pipe forward the last argument, it looks very nice and clean:

[4;5;6]
    |> List.append [1;2;3]

// Result: [1;2;3;4;5;6]

If I want to pipe-forward the first argument, I can use a "fun x ->" function, but I am just curious if there is a cleaner way.

[1;2;3]
    |> fun x -> List.append x [4;5;6]

// Result: [1;2;3;4;5;6] 

Thank you very much.


P.S. My coworker just helped me with this problem. But I would appreciate any help if you have any suggestions for an F# beginner. Thank you.

Community
  • 1
  • 1
CH Ben
  • 1,583
  • 13
  • 23

4 Answers4

13

When the order of arguments is inconvenient, you can always transform the function itself to make it take the arguments in a different order:

let flip f x y = f y x

[1; 2; 3]
    |> (flip List.append) [4; 5; 6]

Transforming functions is useful in many contexts. It is a functional programmer's bread and butter.

But in the end, I believe, readability is most important: programs are read incomparably more often than they are written. When I find myself in a similar situation, and I don't want to name the intermediate value or use a lambda expression for some reason, I define myself a function that would be intuitive to understand:

let prependTo a b = List.append b a

[1; 2; 3] 
   |> prependTo [4; 5; 6]
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • 1
    This is the best approach. The `flip` function is perfectly readable **IF** you know that only F# experts are going to read your code. (Which does sometimes happen). But if your coworkers are going to see this, use something simpler like this `prependTo` function instead, because readability counts. – rmunn Mar 15 '17 at 07:59
12

All the existing answers provide good solution to the problem.

However, I think that if you want to use pipe to specify the first argument of a function, most of the time, it is not a good idea and you should just use an ordinary call without pipes:

List.append [1;2;3] [4;5;6]

The reason is that the "piping pattern" lets you write code that performs series of transformations on some data structure (list, etc.). Libraries designed to support the piping pattern will take the "main input" as the last argument to support pipe.

If you are piping into an argument that is not last, it means that you are breaking this regularity, so you are no longer passing one data structure through a series of transformations. This will make your code confusing because "it looks like a pipe, but it is not really a pipe".

Of course, this is not always the case and there are situations where you need this, but as a rule of thumb, I'd only use pipe when it fits.

TheInnerLight
  • 12,034
  • 1
  • 29
  • 52
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
4

Just use a lambda:

[1;2;3] |> fun l -> l @ [4;5;6]

or point-free:

[1;2;3] |> (@) <| [4;5;6] // or
([1;2;3], [4;5;6]) ||> (@)

or as a normal function:

let foo l = l @ [4;5;6]
let bar = foo [1;2;3]
ildjarn
  • 62,044
  • 9
  • 127
  • 211
1

After asking my coworker, he suggested:

[1;2;3]
    |> List.append
    <| [4;5;6]
// Result: [1;2;3;4;5;6]

If you have any other suggestions to an F# beginner like me, I would greatly appreciate your help. Thank you.

CH Ben
  • 1,583
  • 13
  • 23
  • 5
    I don't actually recommend this approach. The `<|` operator is not a good habit to get into, because its precedence is actually *different* from `|>`. This is a trap that will surprise you later when you don't expect it. – rmunn Mar 15 '17 at 08:01
  • 2
    Agreed, I think that combining forward/backward pipe operators (and composition operators) in the same expression makes for very confusing and unreadable code. By all means, use both directions, just not in the same expression. – TheInnerLight Mar 15 '17 at 09:57