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)
Comments
- Anonymous
January 02, 2013
Don, do you have a reference that I could go to to understand this post more. I'm just getting started with functional programming and F# and I'm trying to pickup as much as I can. The type definitions are a little confusing because it seems that you're redefining the IQueryable of T type in System.Linq. Or are these types providing extension methods to any type that implements IQueryable of T? Sorry for the newbie questions. :)