Why does the pipe operator work?

前端 未结 4 1099
一个人的身影
一个人的身影 2021-02-13 12:44

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         


        
相关标签:
4条回答
  • 2021-02-13 13:16

    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.

    0 讨论(0)
  • 2021-02-13 13:27

    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)
    
    0 讨论(0)
  • 2021-02-13 13:30

    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]
    
    0 讨论(0)
  • 2021-02-13 13:36

    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).

    0 讨论(0)
提交回复
热议问题