Why does the pipe operator work?

匿名 (未验证) 提交于 2019-12-03 03:05:02

问题:

If the pipe operator is created like this:

let (|>) f g = g f 

And used like this:

let result = [2;4;6] |> List.map (fun x -> x * x * x) 

Then what it seems to do is take List.Map and puts it behind (fun x -> x * x * x) And doesn't change anything about the position of [2;4;6]

So now it looks like this:

let result2 = [2;4;6] (fun x -> x * x * x) List.map 

However this doesn't work.

I am just learning f# for the first time now. And this bothered me while reading a book about f#. So I might learn what I'm missing later but I decided to ask anyway.

It is obvious though that I am missing something major. Since I can easily recreate the pipe operator. But I don't get why it works. I might embarrass myself very soon as I learn more. Oh well.

回答1:

The pipe operator is simply syntactic sugar for chained method calls. It's very similar to how linq expressions are expressed in C#.

Explanation from here:

Forward Pipe Operator I love this guy. The Forward pipe operator is simply defined as:

let (|>) x f = f x 

And has a type signature:

'a -> ('a -> 'b) -> 'b 

Which translates to: given a generic type 'a, and a function which takes an 'a and returns a 'b, then return the application of the function on the input.

Rather than explaining this, let me give you an example of where it can be used:

// Take a number, square it, then convert it to a string, then reverse that string let square x         = x * x let toStr (x : int)  = x.ToString() let rev   (x : string) = new String(Array.rev (x.ToCharArray()))  // 512 -> 1024 -> "1024" -> "4201" let result = rev (toStr (square 512)) 

The code is very straight forward, but notice just how unruly the syntax looks. All we want to do is take the result of one computation and pass that to the next computation. We could rewrite it by introducing a series of new variables:

let step1 = square 512 let step2 = toStr step1 let step3 = rev step2 let result = step3 

But now you need to keep all those temporary variables straight. What the (|>) operator does is take a value, and 'forward it' to a function, essentially allowing you to specify the parameter of a function before the function call. This dramatically simplifies F# code by allowing you to pipe functions together, where the result of one is passed into the next. So to use the same example the code can be written clearly as:

let result = 512 |> square |> toStr |> rev 

Edit:

In F# what you're really doing with a method call is taking a function and then applying it to the parameter that follows, so in your example it would be List.map (fun x -> x * x * x) is applied to [2;4;6]. All that the pipe operator does is take the parameters in reverse order and then do the application reversing them back.

function: List.map (fun x -> x * x * x) parameter: [2;4;6]

Standard F# call syntax: f g

Reversed F# call syntax: g f

Standard:

let var = List.map (fun x -> x * x * x) [2;4;6] 

Reversed:

let var = [2;4;6] |> List.map (fun x -> x * x * x) 


回答2:

The brackets around |> mean it is an infix operator so your example could be written

let result = (|>) [2;4;6] (List.map (fun x -> x * x * x)) 

Since |> applies its first argument to the second, this is equivalent to

let result = (List.map (fun x -> x * x)) [2;4;6] 


回答3:

As others have said above, basically you're misunderstanding what result2 would resolve to. It would actually resolve to

List.map (fun x -> x * x * x) [2;4;6]

List.map takes two arguments: a function to apply to all elements in a list and a list. (fun x -> x * x * x) is the first argument and [2;4;6] is the second.

Basically just put what's on the left of |> after the end of what's on the right.



回答4:

If you enter your definition of |> into fsi and look at the operator's signature derived by type inference you'll notice val ( |> ) : 'a -> ('a -> 'b) -> 'b, i.e. argument 'a being given to function ('a -> 'b) yields 'b.

Now project this signature onto your expression [2;4;6] |> List.map (fun x -> x * x * x) and you'll get List.map (fun x -> x * x * x) [2;4;6], where the argument is list [2;4;6] and the function is partially applied function of one argument List.map (fun x -> x * x * x).



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!