Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pipeline operator refuses to work

I can't understand why this

let data = 
    JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(
         File.ReadAllText <| Path.Combine(myPath, "ejv.json"))

is ok, while this

let data = 
    JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>> 
    <| File.ReadAllText
    <| Path.Combine(myPath, "ejv.json")

Gives me two errors, first is:

First error

and second is:

Second error

What did I do wrong?

UPDATE @Patryk Ćwiek suggested a good edit, which seems to fix errors with types:

let data = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>> 
       << File.ReadAllText 
       <| Path.Combine(myPath, "ejv.json")

But it produces another puzzling message: Unexpected type arguments. Here's a screenshot:

Unexpected type arguments.

I can easily get rid of it removing <Dictionary<string, Dictionary<string, string>>> but in this case my data is of object type, not a Dictionary. Can I somehow save type information an use pipelining as well?

SOLUTION

Thanks to @Patryk Ćwiek solution is as follows:

let d<'a>(s:string) = JsonConvert.DeserializeObject<'a>(s)
let data = 
    Path.Combine(myPath, "ejv.json") 
    |> File.ReadAllText 
    |> d<Dictionary<string, Dictionary<string, string>>> with 

I don't know why, but without that alias d it doesn't work.

like image 915
Rustam Avatar asked Dec 25 '13 10:12

Rustam


1 Answers

As far as I know, the evaluation goes from left to right, so your second expression is an equivalent of:

let data = 
    (JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>> <| File.ReadAllText)
    <| Path.Combine(myPath, "ejv.json")

Notice how the operations flow. That means that you're passing a function to DeserializeObject first, probably not what you meant to do.

When you do this:

let data = 
    JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>> 
    <| (File.ReadAllText <| Path.Combine(myPath, "ejv.json"))

it will work. It's an equivalent of your first option.

Other solution is to invert piping to make the data flow more naturally:

let data = Path.Combine(myPath, "ejv.json") 
           |> File.ReadAllText 
           |> JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>

Edit

I've overlooked one option, that's not-so-commonly used. You can use function composition operator << to make it work, too:

let data = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>> 
           << File.ReadAllText 
           <| Path.Combine(myPath, "ejv.json")

It essentially combines DeserializeObject : string -> 'a and ReadAllText : string -> string into (unnamed in the example above) function f : string -> 'a, where f(s) = DeserializeObject(ReadAllText(s)). Then it feeds the result of Path.Combine via pipe into f.

like image 109
Patryk Ćwiek Avatar answered Sep 20 '22 17:09

Patryk Ćwiek