Good afternoon, all!
So I've been playing around with ways of casting a .NET Collection to a functional data structure. The best I've been able to get is to cast it to a seq first and to whatever I want after that.
The problem is that this seems to break type inference, which is obviously unsafe.
Example:
let a = new System.DirectoryServices.DirectorySearcher("<query>") in
let entries = a.FindAll ()
let entries_list =
let (entries_seq : seq<obj>) = Seq.cast entries_list in
Seq.toList entries_Seq
in
entries_list (* list of AD objects found from query, has type obj *)
In order to do anything useful with entries_list, I'd have to do:
entries_list :?> SearchResult
Trying to generalise it to a seq<'a> fails, since the compiler still requires that I statically type its enumerator (which makes sense).
Is there any way of avoiding this? I'm starting to think this is a limitation of using .NET data structures in a functional manner.
Sorry if this is a novice question; I'm green to F# and functional programming in general (and am loving it!). Cheers!
As Daniel says, you typically shouldn't need to use Seq.cast
because most collections will already implement the generic seq<'t>
interface. However, there are several .NET collection types that were built before the introduction of generics in .NET 2.0 which only implement the non-generic IEnumerable
interface. The F# compiler actually has some special logic in for
loops called "enumerable extraction" to make working against these kinds of collections a bit easier. Therefore, if you're only dealing with one of these collection types (e.g. you're working with DirectoryServices.SearchResultCollections
a lot), then it probably makes sense to simply create a simple helper function:
let typedSearchResults (s:SearchResultCollection) =
seq { for result in s -> result }
which you can then use instead of Seq.cast
for this particular collection type.
If you're using lots of different old-style collections in the same project, then you can use some fancy F# features to make a generic Seq.cast
alternative:
module Seq =
let inline inferCast s =
// constrain ^t to have an Item indexed property (which we don't actually invoke)
let _ = fun x -> (^t : (member Item : int -> ^v with get) (x, 0))
let e = (^t : (member GetEnumerator : unit -> ^e) s)
seq { while (^e : (member MoveNext : unit -> bool) e) do
yield (^e : (member Current : obj) e) :?> ^v }
Now you can use Seq.inferCast
instead of Seq.cast
, and the correct item type will be inferred for you. This is probably overkill in your case, though.
Most .NET collections implement IEnumerable<T>
(aliased as seq<'T>
in F#) but occasionally you'll run across one that only implements the non-generic interface IEnumerable
. SearchResultCollection
is one such type. You can use Seq.cast
to convert those collections to seq<'T>
, making it possible to use them with functions in the Seq
module.
open System.DirectoryServices
use searcher = new DirectorySearcher("<query>")
let entries = searcher.FindAll() |> Seq.cast<SearchResult>
let entries_list = Seq.toList entries
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With