mitomed
mitomed

Reputation: 2066

Transform Forward Pipe Operator syntax to "regular" combination of functions

First steps with F# so it's probably one of the most basic questions of F# in the site. Following a tutorial and I came across this "slide".

When it uses

//"Date,Open,High,Low,Close,Volume,Adj Close"
let stockData =
    [ 
      "2012-03-30,32.40,32.41,32.04,32.26,31749400,32.26";
      "2012-03-29,32.06,32.19,31.81,32.12,37038500,32.12";
      "2012-03-28,32.52,32.70,32.04,32.19,41344800,32.19";
      "2012-03-27,32.65,32.70,32.40,32.52,36274900,32.52";
      // And so on (don't even know now how to comment out in F#!)
    ]

let splitCommas (x:string) =
    x.Split([|','|])

stockData
|> List.map splitCommas
|> List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
|> (fun x -> x.[0])

As part of the description it says

Finally, you project the date from the maximum row using List.map and a projection function of (fun x -> x.[0]).

And as I can't see List.map for that I presumed it was using another syntax, so I tried to rewrite it with no Forward pipe operator, but I can't make it work

List.map(fun x-> x.[0])
    (List.maxBy(fun x -> abs(float x.[1] - float x.[4]))
        List.map splitCommas stockData)

I'm getting this error

stdin(54,18): error FS0752: The operator 'expr.[idx]' has been used on an object of indeterminate type based on information prior to this program point. Consider adding further type constraints

And if I try this

List.map(fun (x: string) x-> x.[0])
    (List.maxBy(fun x -> abs(float x.[1] - float x.[4]))
        List.map splitCommas stockData)

I get this one

stdin(71,26): error FS0038: 'x' is bound twice in this pattern

If I put the constraint in the second line again the first error I posted...

Any help on this probably very obvious question?

thanks

Edit:

Using code given in the first answer I'm getting now this error

stdin(195,22): error FS0001: This expression was expected to have type string list
but here has type string array

As far as I understand both Lee and mattnewport answers make sense and are in the same path, and that's why I don't know how to fix that error.

Upvotes: 1

Views: 348

Answers (3)

Søren Debois
Søren Debois

Reputation: 5688

Wrt. the question title: You can mechanically translate forward pipes like this. When you see

x |> f 

you write instead

f x

When you have more than one pipe, you have to take into account that they associate to the left, so

x |> f |> g

is really

(x |> f) |> g

and becomes

g (f x)

Now we can translate you original example mechanically, and we get:

stockData
|> List.map splitCommas
|> List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
|> (fun x -> x.[0])
==>    
List.map splitCommas stockData
|> List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
|> (fun x -> x.[0])
==>
List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
           (List.map splitCommas stockData)
|> (fun x -> x.[0])
==>
(fun x -> x.[0]) (List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
                             (List.map splitCommas stockData))

We can then fold in the (fun x -> x.[0]), obtaining

(List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
            (List.map splitCommas stockData)).[0]

This demonstrates why we have the pipe operators: the straight syntax quickly becomes very difficult to understand. Also, whereas the original was perfectly typable, the f# type elaborator won't be able to type this without additional type annotations; see comment by @mattnewport under his answer above.

Upvotes: 2

mattnewport
mattnewport

Reputation: 14057

One of the reasons the forward pipe operator is used so much in F# is that it helps avoid the type deduction problems you're seeing when you try to re-arrange the code to use normal function call syntax. You've got the right idea applying the string type constraint to the x argument but you've got an additional x in there that's leading to your second error.

The description of the original code appears to be misleading you somewhat by mentioning List.map which isn't being used for this step. The final projection function is actually just selecting the 0th element out of the string array returned by List.maxBy, which gives you the date of the largest absolute difference between the opening and closing prices of the stock. In the projection function x is of type string[] not string:

> stockData
  |> List.map splitCommas
  |> List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
val it : string [] =
  [|"2012-03-13"; "32.24"; "32.69"; "32.15"; "32.67"; "48951700"; "32.67"|]

> it |> (fun x -> x.[0]);;
val it : string = "2012-03-13"

Upvotes: 2

Lee
Lee

Reputation: 144126

The function you're passing to List.map has two arguments called x, remove the second:

(fun (x: string) -> x.[0])

Note you also need to provide the argument for the function passed to List.maxBy:

List.map(fun (x: string) -> x.[0])
    (List.maxBy(fun (x: string array) -> abs(float x.[1] - float x.[4]))
        (List.map splitCommas stockData))

Upvotes: 1

Related Questions