We have already seen many examples of F#’s use with the .NET libraries, such as using
System.Net.WebRequest and
System.Text.RegularExpressions. And the integration was indeed seamless.
For more complex requirements, F# natively supports .NET classes, interfaces, and structures, so the interop is still very straightforward. For example, you can write an
ISomething interface in C# and have the implementation be done in F#.
But not only can F# call into existing .NET code, it can also expose almost any .NET API back to other languages. For example, you can write classes and methods in F# and expose them to C#, VB or COM. You can even do the above example backwards – define an
ISomething interface in F# and have the implementation be done in C#! The benefit of all this is that you don’t have to discard any of your existing code base; you can start using F# for some things while retaining C# or VB for others, and pick the best tool for the job.
In addition to the tight integration though, there are a number of nice features in F# that often make working with .NET libraries more convenient than C# in some ways. Here are some of my favorites:
TryParseand
TryGetValuewithout passing an “out” parameter.
IDisposablewithout creating a concrete class.
The
TryParse and
TryGetValue functions for values and dictionaries are frequently used to avoid extra exception handling. But the C# syntax is a bit clunky. Using them from F# is more elegant because F# will automatically convert the function into a tuple where the first element is the function return value and the second is the “out” parameter.
//using an Int32 let (i1success,i1) = System.Int32.TryParse("123"); if i1success then printfn "parsed as %i" i1 else printfn "parse failed" let (i2success,i2) = System.Int32.TryParse("hello"); if i2success then printfn "parsed as %i" i2 else printfn "parse failed" //using a DateTime let (d1success,d1) = System.DateTime.TryParse("1/1/1980"); let (d2success,d2) = System.DateTime.TryParse("hello"); //using a dictionary let dict = new System.Collections.Generic.Dictionary<string,string>(); dict.Add("a","hello") let (e1success,e1) = dict.TryGetValue("a"); let (e2success,e2) = dict.TryGetValue("b");
In C# (and .NET in general), you can have overloaded methods with many different parameters. F# can have trouble with this. For example, here is an attempt to create a
StreamReader:
let createReader fileName = new System.IO.StreamReader(fileName) // error FS0041: A unique overload for method 'StreamReader' // could not be determined
The problem is that F# does not know if the argument is supposed to be a string or a stream. You could explicitly specify the type of the argument, but that is not the F# way!
Instead, a nice workaround is enabled by the fact that in F#, when calling methods in .NET libraries, you can specify named arguments.
let createReader2 fileName = new System.IO.StreamReader(path=fileName)
In many cases, such as the one above, just using the argument name is enough to resolve the type issue. And using explicit argument names can often help to make the code more legible anyway.
There are many situations where you want to use pattern matching against .NET types, but the native libraries do not support this. Earlier, we briefly touched on the F# feature called “active patterns” which allows you to dynamically create choices to match on. This can be very for useful .NET integration.
A common case is that a .NET library class has a number of mutually exclusive
isSomething,
isSomethingElse methods, which have to be tested with horrible looking cascading if-else statements. Active patterns can hide all the ugly testing, letting the rest of your code use a more natural approach.
For example, here’s the code to test for various
isXXX methods for
System.Char.
let (|Digit|Letter|Whitespace|Other|) ch = if System.Char.IsDigit(ch) then Digit else if System.Char.IsLetter(ch) then Letter else if System.Char.IsWhiteSpace(ch) then Whitespace else Other
Once the choices are defined, the normal code can be straightforward:
let printChar ch = match ch with | Digit -> printfn "%c is a Digit" ch | Letter -> printfn "%c is a Letter" ch | Whitespace -> printfn "%c is a Whitespace" ch | _ -> printfn "%c is something else" ch // print a list ['a';'b';'1';' ';'-';'c'] |> List.iter printChar
Another common case is when you have to parse text or error codes to determine the type of an exception or result. Here’s an example that uses an active pattern to parse the error number associated with
SqlExceptions, making them more palatable.
First, set up the active pattern matching on the error number:
open System.Data.SqlClient let (|ConstraintException|ForeignKeyException|Other|) (ex:SqlException) = if ex.Number = 2601 then ConstraintException else if ex.Number = 2627 then ConstraintException else if ex.Number = 547 then ForeignKeyException else Other
Now we can use these patterns when processing SQL commands:
let executeNonQuery (sqlCommmand:SqlCommand) = try let result = sqlCommmand.ExecuteNonQuery() // handle success with | :?SqlException as sqlException -> // if a SqlException match sqlException with // nice pattern matching | ConstraintException -> // handle constraint error | ForeignKeyException -> // handle FK error | _ -> reraise() // don't handle any other cases // all non SqlExceptions are thrown normally
F# has another useful feature called “object expressions”. This is the ability to directly create objects from an interface or abstract class without having to define a concrete class first.
In the example below, we create some objects that implement
IDisposable using a
makeResource helper function.
//."
The example also demonstrates how the “
use” keyword automatically disposes a resource when it goes out of scope. Here is the output:
using first resource do something with inner resource 1 inner resource 1 disposed do something with inner resource 2 inner resource 2 disposed do something with inner resource 3 inner resource 3 disposed using second resource done. second resource disposed first resource disposed
The ability to create instances of an interface on the fly means that it is easy to mix and match interfaces from existing APIs with pure F# types.
For example, say that you have a preexisting API which uses the
IAnimal interface, as shown below.
type IAnimal = abstract member MakeNoise : unit -> string let showTheNoiseAnAnimalMakes (animal:IAnimal) = animal.MakeNoise() |> printfn "Making noise %s"
But we want to have all the benefits of pattern matching, etc, so we have created pure F# types for cats and dogs instead of classes.
type Cat = Felix | Socks type Dog = Butch | Lassie
But using this pure F# approach means that that we cannot pass the cats and dogs to the
showTheNoiseAnAnimalMakes function directly.
However, we don’t have to create new sets of concrete classes just to implement
IAnimal. Instead, we can dynamically create the
IAnimal interface by extending the pure F# types.
// now mixin the interface with the F# types type Cat with member this.AsAnimal = { new IAnimal with member a.MakeNoise() = "Meow" } type Dog with member this.AsAnimal = { new IAnimal with member a.MakeNoise() = "Woof" }
Here is some test code:
let dog = Lassie showTheNoiseAnAnimalMakes (dog.AsAnimal) let cat = Felix showTheNoiseAnAnimalMakes (cat.AsAnimal)
This approach gives us the best of both worlds. Pure F# types internally, but the ability to convert them into interfaces as needed to interface with libraries.
F# gets the benefit of the .NET reflection system, which means that you can do all sorts of interesting things that are not directly available to you using the syntax of the language itself. The
Microsoft.FSharp.Reflection namespace has a number of functions that are designed to help specifically with F# types.
For example, here is a way to print out the fields in a record type, and the choices in a union type.
open System.Reflection open Microsoft.FSharp.Reflection // create a record type... type Account = {Id: int; Name: string} // ... and show the fields let fields = FSharpType.GetRecordFields(typeof<Account>) |> Array.map (fun propInfo -> propInfo.Name, propInfo.PropertyType.Name) // create a union type... type Choices = | A of int | B of string // ... and show the choices let choices = FSharpType.GetUnionCases(typeof<Choices>) |> Array.map (fun choiceInfo -> choiceInfo.Name)