QuickLinq Helpers

Sometimes in F# it is useful to use LINQ extension methods such as .Take(n), .Skip(n) and .Count().

However, in F# some LINQ extension methods such as .Where(...) and .Select(...) require a type annotation on the lambda variable. This is one of the relatively rare cases where F# code requires a type annotation where other languages do not, and is due to differences in the process of type inference and method overload resolution.

Luckily, there is a simple useful workaround. First add the library code below to your file or library of F# helper functions. After this, you will be able to use the methods .SelectQ, .WhereQ and other 'QuickLinq' variations without needing type annotations. Simply choose the 'Q' variation where it is available.

This becomes particularly important when working with provided data sources, where giving type annotations can be problematic.  For example when using the Freebase type provider, the type names can be quite long, so

            .Select(fun (x: FreebaseData.ServiceTypes.Chemistry.Chemistry.Chemical_elementData) -> x.``Atomic number``)

becomes

            .SelectQ(fun x -> x.``Atomic number``)

Here's the helper code. You can also find it at fssnip.net/g0

[<AutoOpen>]

module QuickLinqHelpers =

    open System

    open System.Linq

    open System.Linq.Expressions

    open System.Collections.Generic

    type IQueryable<'T> with

        /// A version of Queryable.OrderBy which does not require a type annotation on the argument when used from F#

        member x.OrderByQ(keySelector : Expression<Func<'T,'TKey>>) = System.Linq.Queryable.OrderBy(x,keySelector)

        /// A version of Queryable.OrderByDescending which does not require a type annotation on the argument when used from F#

        member x.OrderByDescendingQ(keySelector : Expression<Func<'T,'TKey>>) = System.Linq.Queryable.OrderByDescending(x,keySelector)

        /// A version of Queryable.Select which does not require a type annotation on the argument when used from F#

        member x.SelectQ(selector : Expression<Func<'T,'TResult>>) = System.Linq.Queryable.Select(x,selector)

        /// A version of Queryable.SelectMany which does not require a type annotation on the argument when used from F#

        member x.SelectManyQ(selector : Expression<Func<'T,IEnumerable<'TResult>>>) = System.Linq.Queryable.SelectMany(x,selector)

        /// A version of Queryable.SkipWhile which does not require a type annotation on the argument when used from F#

        member x.SkipWhileQ(predicate : Expression<Func<'T,bool>>) = System.Linq.Queryable.SkipWhile(x,predicate)

        /// A version of Queryable.TakeWhile which does not require a type annotation on the argument when used from F#

        member x.TakeWhileQ(predicate : Expression<Func<'T,bool>>) = System.Linq.Queryable.TakeWhile(x,predicate)

        /// A version of Queryable.Where which does not require a type annotation on the argument when used from F#

        member x.WhereQ(predicate : Expression<Func<'T,bool>>) = System.Linq.Queryable.Where(x,predicate)

 

 

    type IOrderedQueryable<'T> with

        /// A version of Queryable.ThenBy which does not require a type annotation on the argument when used from F#

        member x.ThenByQ(keySelector : Expression<Func<'T,'TKey>>) = System.Linq.Queryable.ThenBy(x,keySelector)

        /// A version of Queryable.ThenByDescending which does not require a type annotation on the argument when used from F#

        member x.ThenByDescendingQ(keySelector : Expression<Func<'T,'TKey>>) = System.Linq.Queryable.ThenByDescending(x, keySelector)