Enumerable Types in F# January, 2010
Functional programming never ceases to amaze me. The more I code in F# the more I've come to appreciate how succinct and terse you can write your code. I'll sometimes start out with an imperative solution but there always seems to be a functional one that is fewer lines of code and much clearer. I'm working on rewriting a project in F#, that allows you to interact with USB HID devices, in order to learn F#. One of the things I ran into is creating an enumerable and disposable type that wraps a couple of API calls.
type DeviceInfoSet(connectedOnly:bool) = let handle = GetDeviceInfoSet connectedOnly member private c.ToSeq = Seq.unfold (fun index -> let info, more = GetDeviceInfo handle index match more with | true -> Some (info, index + 1) | _ -> None ) 0 interface IEnumerable with member c.GetEnumerator () = c.ToSeq.GetEnumerator() :> IEnumerator interface IEnumerable
with member c.GetEnumerator () = c.ToSeq.GetEnumerator() interface IDisposable with member c.Dispose () = handle.Dispose()
Here we have a constructed class type that takes in a parameter required by the native call. Unlike a C# type definition, the construction doesn't occur in a constructor function but in the body of the type. The ToSeq property generates the sequence. I absolutely love the unfold function, it is so powerful. Here the native method requires an integer to be incremented until an error code is returned to indicate that there are no more records (Not shown). As long as there are more records we return the item and increment the index. Once there are no more we return None. Now we get to the interface implementation. This is pretty straight forward, the only thing you have to do with the non generic IEnumerable implementation is downcast it to IEnumerator with the :> operator.
Here is an example of the usage:
let enumerate = use devices = new DeviceInfoSet(false) devices |> Seq.iter (fun item -> Console.WriteLine(item.ClassGuid.ToString()))The "use" keyword is equivalent to a using statement in C#. Once the "devices" value goes out of scope it is disposed of.