6

I have googlet a bit, and I haven't found what I was looking for. As expected. My question is, is it possible to define a F# pipeline placeholder? What I want is something like _ in the following:

let func a b c = 2*a + 3*b + c

2 |> func 5 _ 6

Which would evaluate to 22 (2*5 + 3*2 + 6).

For comparison, check out the magrittr R package: https://github.com/smbache/magrittr

torbonde
  • 2,459
  • 1
  • 15
  • 17
  • I do not think this scenario makes sense unless your `func` took a tuple: `func (a,b,c)`. Consider what the fsi shows you when you type your function `val func : a:int -> b:int -> c:int -> int`, which means that you must define b in order to get the function which takes c. – Phillip Scott Givens Feb 14 '14 at 17:25

6 Answers6

6

This is (unfortunately!) not supported in the F# language - while you can come up with various fancy functions and operators to emulate the behavior, I think it is usually just easier to refactor your code so that the call is outside of the pipeline. Then you can write:

let input = 2
let result = func 5 input 6

The strength of a pipeline is when you have one "main" data structure that is processed through a sequence of steps (like list processed through a sequence of List.xyz functions). In that case, pipeline makes the code nicer and readable.

However, if you have function that takes multiple inputs and no "main" input (last argument that would work with pipelines), then it is actually more readable to use a temporary variable and ordinary function calls.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 1
    I agree that this feature would be awesome; so it is unfortunate that it cannot be done properly. I also run into situations where it would be fantastic to be able to do a |> b |> c |> d e . |> f, rather than make temporary variables, or helper functions. – Stefan Feb 17 '14 at 08:44
  • But why does this work? I don't think it should work but it does e.g. `backwardTest a b` is the same as `a - b` `let backwardTest x y = ` `let minus a b = a - b` `minus <| x <| y` – dragonfly02 Dec 27 '15 at 22:09
2

I don't think that's possible, but you could simply use a lambda expression, like

2 |> (fun b -> func 5 b 6)
sloth
  • 99,095
  • 21
  • 171
  • 219
1

You could use a new function like that:

let func a b c = 2*a + 3*b + c
let func2 b = func 5 b 6

2 |> func2
V.B.
  • 6,236
  • 1
  • 33
  • 56
  • If that could happen automatically, that would be great. But doing it by myself, again and again, is not particularly pretty either. – torbonde Feb 14 '14 at 13:36
  • Just curios about the context of the parent task and how often you have to do this per day? – V.B. Feb 14 '14 at 13:42
  • There is no current task. I am not in need of it right now. But it is something I have needed several times. If I have ten `|>`'s beneath each other - in five of which the value should not be passed into the last parameter - then this small feature would make the code a lot prettier, and a lot easier to read. I don't have an estimate of how many times per day. But I work with F# every day, as part of my work. Hence, it could become very useful, very often. – torbonde Feb 14 '14 at 13:49
  • For other people it will be harder to read, because one would expect lambda for a single call and a curried function for repeated calls with the same fixed variables. Some "ad hoc magic" could be confusing – V.B. Feb 14 '14 at 13:52
  • Yes, you would, if you are an experienced F# programmer. If it suddenly became part of the standard F# syntax, you would too, at first. But then you would become used to it. One point I have to make is, that the people I work with are not experienced F# programmers. – torbonde Feb 14 '14 at 14:15
  • 1
    I voted this up, but wanted to say that I do not think that this is currying. I think this is defining a new function which calls the old function with specific parameters. Currying happens twice when you call `func a b c`, because this is not a tuple. The currying is when you call `func a` and it returns a function which takes `b`. The function which takes `b` returns a function which takes `c` and returns a number. – Phillip Scott Givens Feb 14 '14 at 17:17
  • 1
    @PhillipScottGivens thanks for such a detailed clarification! I just needed to name this fast, but missed the point – V.B. Feb 14 '14 at 17:25
1

Here's a point-free approach:

let func a b c = 2*a + 3*b + c
let func' = func 5 >> (|>) 6
let result = 2 |> func'
// result = 22

I have explained it in details here.

Be aware, however, that someone who would work with your code will not quickly grasp your intent. You may use it for purposes of learning the deeper aspects of the language, but in real-world projects you will probably find a straightforward approach suitable better:

let func' b = func 5 b 6
Community
  • 1
  • 1
Be Brave Be Like Ukraine
  • 7,596
  • 3
  • 42
  • 66
0

@Dominic Kexel's right on the money. If the object isn't really the placement of a placeholder in the chain of arguments, which could have been achieved by a lambda function, but changing their order, then it's more a case of flip than pipe.

From the simple two-argument case

let flip f b a = f a b
// val flip : f:('a -> 'b -> 'c) -> b:'b -> a:'a -> 'c

we need to derive a function

let flip23of3 f a c b = f a b c
// val flip23of3 : f:('a -> 'b -> 'c -> 'd) -> a:'a -> c:'c -> b:'b -> 'd

in order to flip the second and third argument. This could have also been written

let flip23of3' f = f >> flip

let func a b c = 2*a + 3*b + c
2 |> flip23of3 func 5 6
// val it : int = 22
kaefer
  • 5,491
  • 1
  • 15
  • 20
-1

I have given it a try myself. The result is not perfect, but it is as close as I have gotten:

let (|.|) (x: 'a -> 'b -> 'c) (y: 'b) = fun (a: 'a) -> x a y

let func (a:string) b (c:int) = 2.*(float a) + b + 5.*(float c)

let foo = func "4" 9. 5
printfn "First: %f" foo

let bar =
    "4"
    |> ((func |.| 9.) |.| 5)

printfn "Second: %f" bar

let baz =
    9.
    |> (func "4" |.| 5)
printfn "Third: %f" baz

The output is, as expected

First: 42.000000
Second: 42.000000
Third: 42.000000
torbonde
  • 2,459
  • 1
  • 15
  • 17
  • 1
    IMHO, This is way much harder to read than a lambda/curried functions and longer to write. It adds unneeded complexity and required to learn a new operator – V.B. Feb 14 '14 at 13:55
  • It is much harder to read than a lambda function, I agree. It is however the closest to what I asked for. If I could write as I wrote in my post, that would be the easiest to read. Curried functions are not really part of the picture, since they are applicable in completely different situations, that where I would expect this. – torbonde Feb 14 '14 at 14:13
  • 3
    IMO doing it this way clearly favors the person writing the code rather than the one who has to maintain it. As a general rule, I write for maintainability rather than convenience of the person writing the code. – N_A Feb 14 '14 at 15:14