Search code examples
f#query-expressions

order of operators in F# query expression


Does the order of query expression operators matter? Idk, but sometimes (in some selections) it does, but sometimes it doesn't (or maybe it does but implicitly handles some particular occasions).

Is is mandatory that select operator must go last? in pretty much every combination it complains if you don't write it as a last statement, but in case of take n, this operator can go after select

I'm just interested in how the process of execution acts?

This brings me to another question. If it iterates over Iterable collection, and thus on the first iteration it selects some one(first) value, how order works on that one(first) value? it would be clear if first it returned sequence, and then executed order on that sequence.. but seems like it executes sortBy at every iteration (?). I'm interested in what the design of the executed algorithm is.

Here is my example of Query Expression.

let sq = query {
   for p in datasource do
   where p.age>20
   sortBy p.age
   select p
}

Explanations would be greatly appreciated.

Thanks


Solution

  • We don't need to guess, we can find out.

    let sample = Seq.init 10 (fun i -> i * 10) |> Seq.map (fun i -> { age =  i }) 
    let sq = query {
       for p in sample do
       where (p.age > 20)       
       sortBy p.age
       select p
    }
    
    sq |> Seq.toList |> ignore
    

    The generated IL (cleaned up) looks like

    IL_004e: newobj instance void Program/sq@16::.ctor(class [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder)
    IL_0053: callvirt instance [...] For<class Program/Person,      
    IL_005d: callvirt instance [...] Where<class Program/Person,    
    IL_0067: callvirt instance [...] SortBy<class Program/Person,   
    IL_0071: callvirt instance [...] Select<class Program/Person,   
    

    Suppose we change the order of sortBy

    let sq = query {
       for p in sample do
       sortBy p.age
       where (p.age > 20)       
       select p
    }
    

    The new IL will be:

    IL_006c: callvirt instance [...] For<class Program/Person,      
    IL_0076: callvirt instance [...] SortBy<class Program/Person,   
    IL_0080: callvirt instance [...] Where<class Program/Person,    
    IL_008a: callvirt instance [...] Select<class Program/Person,   
    

    You can clearly see that it follows the exact order you define the query in. This wouldn't matter for T-SQL comprehensions because the query will be translated by an Expression visitor, but for object queries, query expressions are pretty much just syntactic sugar for you.