Chapter 18. C#

WHAT'S IN THIS CHAPTER?

  • Calling C# libraries from F#

  • Exploring the complexities of C# libraries

  • Calling F# libraries from C#

  • Structuring F# libraries for C# consumption

  • Avoiding common pitfalls

While F# is definitely useful on its own, if it were unable to leverage programs written in other languages such as C#, it would be far less practical for general use than it could be. And C# programs greatly benefit from using new F# programs. This chapter explains how these two languages can interoperate together and provides some tips to make that interoperation easier.

OVERVIEW

One of the most significant reasons why F# is a compelling functional language is that it is best positioned for broad usage among software developers on the .NET platform. It isn't the only functional language in that it not only can leverage the .NET framework but there are also many others — including implementations of Clojure and Scheme. What sets F# apart is not only "inclusion in the box" as part of Visual Studio, but also the downstream implications of that, meaning it will be available to a broader pool of developers, which leads to broader skill availability, which helps build a talent base that makes organizations more likely to consider using the language.

Of course, none of this would be possible if these more risk-averse organizations could leverage existing investments in C# in their F# programs. Although it would be nice to live in an "ideal programmer world" where one picks and chooses the legacy code base one gets to program to, reality is that no language will ever get significant usage if it can't build on work that has already been done.

CALLING C# LIBRARIES FROM F#

Given a great deal of the code written on the .NET platform is written in C#, it would be almost impossible to write a meaningful F# program without calling C# code at some point. This section covers the simple cases, and more interestingly, some of the more complex cases that tend to be hard to handle from the F# side.

Simple Method Calling Scenarios

The vast majority of F# to C# integration scenarios is intuitive. Consider the following somewhat trivial C# class:

namespace CSharpLibrary
{
    public class CSharpLibraryUsedWithFSharp
    {
        public int SomeMutableProperty { get; set; }
        public int AnotherMutableProperty { get; set; }

        public CSharpLibraryUsedWithFSharp()
        {

        }

        public bool SomeFunction(int firstParam, int secondParam)
        {
            return true;
        }
    }
}
                                               
Simple Method Calling Scenarios

If this class is in a project called CSharpLibrary that compiles to a class library, after referencing the library from your F# project, the following could be written to call SomeFunction:

module FSharpCallingCSharp
open System
open CSharpLibrary
//typical call to C#
let newCSharpObject = new CSharpLibraryUsedWithFSharp()
let someResult = newCSharpObject.SomeFunction(42,69)
                                                         
Simple Method Calling Scenarios

The semantics of dealing with C# objects in F# is not all that different from how you would deal with dealing with C# objects in C#. The last two lines in C# barely look any different at all:

var newCSharpObject = new CSharpLibraryUsedWithFSharp();
var someResult = newCSharpObject.SomeFunction(42,69);

At this level, moving from C# to F# is a simple matter of replacing var with let and removing the semicolons. You might wonder what all the fuss is about! As might be expected, however, there is much more just a little bit behind the scenes. This gets put into sharp relief when attempting to call C# methods using F# pipeline syntax:

//this works
let anotherResult = (42,69) |> newCSharpObject.SomeFunction
//but you can't do this stuff
let wontWork = newCSharpObject.SomeFunction 42 69
let alsoWontWork = 69 |> 42 |> newCSharpObject.SomeFunction
let neitherWillThisAttemptedCurryingExample = 69 |> newCSharpObject.SomeFunction

                                                       
Simple Method Calling Scenarios

What becomes more clear when looking at this in pipeline form is that from F#, a method with multiple parameters is actually a method with a single parameter that is presented from F# in the form of a tuple. As a result of this, when using C# functions in F#, the same options around function composition will generally not be available. Consider the following common F# currying example:

let someFSharpFunction a b = a + b
let curriedFunction = 42 |> someFSharpFunction
let actualResult = 69 |> curriedFunction

Although the preceding is a simple exercise in F#, doing so with methods defined in C# is not possible in a way that most people would consider elegant, if it is possible at all.

C# Object Construction

Constructing C# objects in F# is mostly trivial:

let newCSharpObject = new CSharpLibraryUsedWithFSharp()

The new facility in F# works mostly how a C# developer would expect it would. Construction with arguments takes the familiar form as well:

let paramsOnly = new CSharpLibraryUsedWithFSharp(11,22)

In C# 3.0, a new feature, object initializers, were introduced, which allow for provision of the initial property values of (usually) mutable properties. Property initialization in F# is not all that different:

let initializersOnly =
  new CSharpLibraryUsedWithFSharp(
    SomeMutableProperty = 55,
    AnotherMutableProperty = 56)

The key difference on the F# side is that parentheses are used, not braces, as you would in C#. If you want to mix explicit constructor parameters with property initializers, the explicit parameters come first:

let paramsAndInitializers =
  new CSharpLibraryUsedWithFSharp(
    11,
    22,
   SomeMutableProperty = 55,
   AnotherMutableProperty = 56)

F#, C#, and null

Idiomatic F# code avoids the use of null as much as possible, in favor of option types for situations where the goal is to express lack of a value. However, there are situations where you may be calling C# code that does, in fact, need to provide a proper C# style null parameter:

//this works
let shouldBeTrue = String.IsNullOrEmpty(null)
//this does not work
let doesNotCompile = String.IsNullOrEmpty(None)

It is important to note that in the world of F#, nulls are to be avoided most of the time. When calling C# code that might return a null, it is often far safer to check for that condition and contain it at the source of the issue, like this:

//pretend this is your normal null spewing C# function
let someRandomStrangeFunctionThatMightReturnNull =
  match DateTime.Now.Day with
  | 31 -> "This is a 31st Day in a 31 Day Month, Yipee"
  | _ -> null

//this is how you will protect against such functions in your F# code
let wontBeNull =
  match someRandomStrangeFunctionThatMightReturnNull with
    | null -> None
    | v -> Some(v)
                                                       
F#, C#, and null

In the preceding example, someRandomStrangeFunctionThatMightReturnNull represents a prototypical C# function that breaks out F# rules about avoiding nulls. This function is not idiomatic F# but helps us illustrate what C# programs sometimes return to us. The wontBeNull value calls that function but does so in a match statement, which converts the null to a None if null is returned, otherwise, producing a Some with the embedded value if such a value exists. This pattern should be used around calls to non F# libraries, including C# programs, that either:

  1. Are known to return null in certain cases

  2. Are from an outside API that might return null — or is unknown about its status as to whether null is returned

A savvy C# programmer may notice that an option with a simple type, such as Some(42), is actually similar to a Nullable<int> in C#. In fact, they are similar in some ways. However, in F#, Some(x) allows x to be any type of object, which allows a programmer to not only be more explicit when stating intent for "lack of an object," but also allows generic functions that deal with both nonobjects and objects to consistently use the same kind of structure to differentiate between Some(T) and None.

F# and C# Methods that Expect Delegates

Calling C# methods that expect delegate parameters, although not hard, is also not entirely obvious. There is no automatic conversion from an F# function to a particular delegate signature in C#. Consider the following C# code:

public class CSharpLibraryUsedWithFSharp
{
    public int SomeMutableProperty { get; set; }
    public int AnotherMutableProperty { get; set; }

    public delegate int BinaryIntegerMathOp(int op1, int op2);

    public CSharpLibraryUsedWithFSharp()
    {

    }

    public int PerformMathOnMyProperties(BinaryIntegerMathOp
op)
    {
        return op(SomeMutableProperty, AnotherMutableProperty);
    }
}
                                             
F# and C# Methods that Expect Delegates

A delegate type has been declared called BinaryIntegerMathOp, which takes two integers and converts them into a single integer. The method PerformAMathOperationOnMyProperties takes a BinaryIntegerMathOp as a parameter and returns the result of the operation.

The following is a way a C# programmer would likely first approach the problem when using F#:

let delegateExample = new CSharpLibraryUsedWithFSharp(
  SomeMutableProperty = 55,
  AnotherMutableProperty = 56)
let addNumbersAsDelegate =
  new CSharpLibraryUsedWithFSharp.BinaryIntegerMathOp(
    fun x y -> x + y
  )
let subNumbersAsDelegate =
  new CSharpLibraryUsedWithFSharp.BinaryIntegerMathOp(
    fun x y -> x - y
  )
let shouldBe111 =
delegateExample.PerformMathOnMyProperties(addNumbersAsDelegate)
let shouldBeMinusOne =
  delegateExample.PerformMathOnMyProperties(subNumbersAsDelegate)
                                                       
F# and C# Methods that Expect Delegates

In this example, specific delegate types are declared and used. This kind of convention may be needed in cases where a method has multiple overloads that take delegates that have the same parameters and return types, to disambiguate which method is to be called. However, if that is not the case, the following simpler and more F# idiomatic syntax may be used:

let simplerForm
  = delegateExample.PerformMathOnMyProperties(fun x y -> x+ y)
let subSimplerForm
  = delegateExample.PerformMathOnMyProperties(fun x y -> x - y)

F# and C# Events

Working with events in C# classes is fairly straightforward. Consider the following class that publishes an event:

public class CSharpLibraryUsedWithFSharp
{
    public delegate void BeerFinishingHandler(string nameOfBeer);
    public event BeerFinishingHandler FinishedABeer;

    public void HaveADrinkingBinge()
    {
        if (FinishedABeer != null)
        {
            FinishedABeer("Belgian Trappist");
            FinishedABeer("Three Philosophers");
            FinishedABeer("Red Hook");
            FinishedABeer("Stella");
            FinishedABeer("Another Stella");
            //as time goes by, standards get lower
            FinishedABeer("Pabst Blue Ribbon");
            FinishedABeer("Schlitz");
            //throw a type mismatch exception?
            FinishedABeer("Zima");
        }
    }
}
                                           
F# and C# Events

In F#, subscribing to events is not unlike one would do in C#, though the syntax is arguably a bit cleaner:

let eventExample = new CSharpLibraryUsedWithFSharp()
eventExample.add_FinishedABeer( fun s -> printfn "Drank a %s" s )
do eventExample.HaveADrinkingBinge()

Subscribing to events is very similar to a typical case of delegate passing, as the preceding code demonstrates with the call to add_FinishedABeer. The method add_FinishedABeer is automatically generated when C# has a public event, comparable to what the += event assignment operator does in C#.

Of course, there are times when you may want to unsubscribe to an event. At first glance, the exposed method remove_FinishedABeer looks promising:

//wont remove the event, since it is a different function instance
eventExample.remove_FinishedABeer( fun s -> printfn "Drank a %s" s )

The problem, of course, is that the remove method needs a means to know what it is removing, because there can be many subscribers to a given event. The preceding code won't create an exception, but because fun s -> printfn "Drank a %s" s gets converted into a different instance of the delegate than the one passed into the add_FinishedABeer method we originally called, it does nothing because the new delegate we create by passing a simple F# function won't be found.

To remove a subscription, we must keep a reference to the original delegate that was passed in to handle the event:

let anotherEventExample = new CSharpLibraryUsedWithFSharp()
let handleMyBeer = new CSharpLibraryUsedWithFSharp.BeerFinishingHandler(
  fun s -> printfn "Drank a %s" s )
anotherEventExample.add_FinishedABeer( handleMyBeer )
//this remove will work, since it is the same delegate instance
anotherEventExample.remove_FinishedABeer( handleMyBeer )
                                                       
F# and C# Events

In this example, an explicit reference to the delegate is created. This works because remove uses reference equality of the delegate to indicate which to remove. The same instance must be used in the remove that was used in the corresponding add for the remove to actually work.

F# to C# Summary

Although F# calling C# is straightforward most of the time, there are some cases where it is not exactly clear how the C# concept translates into F# code that may want to leverage the feature. This is particularly true in areas such as passing around functions, delegates, dealing with events, and dealing with concepts like null that are not mainstream F#. Things get more interesting, however, when you switch to making use of F# libraries in C#.

CALLING F# LIBRARIES FROM C#

A very compelling use case for F#, as organizations start to adapt it, is to integrate specialized modules that do calculation and math that are written in F# into existing C# programs. For the most part, using F# programs from C# code is simple. However, there are certain things that can be done that make F# libraries easier for outside programs to understand, whether they are written in C#, other CLR languages, or even other F# programs.

Basics of Calling F#

Much interaction that occurs when C# calls F# works exactly how you would expect it to. Consider the following type definition in F#:

namespace FSharpLibraryUsedWithCSharp
open System
type FSharpLibrary() =

  member this.MethodCallNoParameters() =
     42
  member this.NoParamsMakesAReadOnlyProperty =
     "I am a property result"
  member this.MethodCallWithParameters x y =
     x + y
  member this.MethodCallWithTupleFormParameters(x,y) =
     x * y
                                                       
Basics of Calling F#

The preceding type and function, as defined in F#, represents various ways that a given F# type may define members. From the C# side, a call to MethodCallNoParameters will look as follows:

var someFSharpObject = new FSharpLibrary();
var simpleFunction = someFSharpObject.MethodCallNoParameters();

The most significant thing to notice is that if the desired signature is to be a zero parameter method, rather than as a property, an empty set of parentheses should be suffixed on the member name in F#. Without the parentheses, the member will take the form of a read-only property:

var withoutParmsIAmAProperty = someFSharpObject.NoParamsMakesAReadOnlyProperty;

Calling members with parameters, whether those parameters are defined in F# in tuple form, operate like normal parameters in C#:

var withParameters = someFSharpObject.MethodCallWithParameters(1, 2);
var withParametersTupleForm =
   someFSharpObject.MethodCallWithTupleFormParameters(1, 2);

It is worth noting that even though internal to an F# program, tuple form would be passed to MethodCallWithTupleFormParameters, doing so from a C# program does not work:

//worth mentioning that this does *not* compile
var passingATupleAsParameters
        = someFSharpObject
                .MethodCallWithTupleFormParameters(new Tuple<int, int>(1, 2));

F# Tuples in C# Programs

Although the practice of using Tuples as external return values in an external API is not recommended, in the event that a Tuple is returned from a function in F# to a C# program, it will take the form of a the new Tuple type in .NET 4.0. Consider the following F# member on the type defined in the previous section:

member this.MemberThatReturnsATuple() =
  ("Aaron","Erickson",37,new DateTime(2010,3,1))

From the point of view of a C# programmer, this member will return an object of type Tuple<string, string, int, DateTime>.

var someTuple = someFSharpObject.MemberThatReturnsATuple();
var firstValue = someTuple.Item1;
var secondValue = someTuple.Item2;
var thirdValue = someTuple.Item3;
var lastValue = someTuple.Item4;

Tuples are very useful, but from the standpoint of C# programs, they are not terribly descriptive, especially outside the context of where they are used, as the names of the items of Item1, 2, 3, and so forth do not really describe much.

Dealing with F# Records from C#

F# records generally come to C# as buckets that hold read-only properties. Consider the following simple record type defined in F#, and a member in FSharpLibrary that returns an instance of the record:

type PersonRecord = {FirstName:string;LastName:string;Age:int}

type FSharpLibrary() =
  member this.MemberReturnsARecord() =
    {FirstName="Aaron";LastName="Erickson";Age=37}

This is a common way for an F# program to return information to a C# program. This is especially true in the sense that it is not as common for F# to deal in objects, and hence, something that would be considered bad practice, the ever vilified "anemic domain" object, is just fine for moving information over an integration seam such as an F# API.

From the perspective of C#, the code is hardly unusual:

var someRecord = someFSharpObject.MemberReturnsARecord();
var firstName = someRecord.FirstName;
var lastName = someRecord.LastName;
var age = someRecord.Age;

Each of the properties in the record type works as expected. As we would expect, the properties of the F# object are immutable, just as they are on the F# side of the fence.

Creation of F# records from C# is simple as well:

//creation of a record (notice the generated ctor)
var aPerson = new PersonRecord("Matthew", "Erickson", 8);

It is notable that when F# compiles a record, it generates the appropriate constructor that does the initial population of the immutable values within the record. Records, used in this way, are not bad things to pass through an API seam, especially for things that would take the role in C# of a data transfer object.

Passing Functions to F# Functions

As understood from previous chapters where function composition is discussed, it is common for F# programs to accept functions as parameters. Consider the following F# member:

member this.MemberThatTakesAFunction a b func =
  func a b

This member is a simplistic member that applies func to a and b. Simple enough in F#, but this kind of thing becomes painful to use from C#:

//calling a member that takes a function as a parameter
var uglyFunc = FuncConvert.ToFSharpFunc<int, FSharpFunc<int, int>>(
  a => FuncConvert.ToFSharpFunc<int,int>(b => a + b)
);
var operationResult = someFSharpObject.MemberThatTakesAFunction(2, 2, uglyFunc);

In the preceding example, few would disagree that the preceding uglyFunc, is misnamed. Not only does it require a reference to FSharp.Core to make the code at all (almost always a bad sign), but it also forces construction of the function in an unintuitive manner. Although it does reveal how the F# composes functions, there is no simple mapping from a more intuitive Func<int, int, int> you would use in C# to the int->int->int that F# is expecting. As a result, it forces the programmer to go through the F# mechanics of composition in C#, and as a result, writing a lot of unidiomatic and confusing C# code in the process.

A better way to allow C# to pass functions to F# code is to use the Func<> or Action<> types, as appropriate:

member this.IsBetterForCSharpInterop(a,b,func:Func<int,int,int>) =
  func.Invoke(a, b)

which can be called from C# and other .NET languages more idiomatically:

var betterResult
       = someFSharpObject.IsBetterForCSharpInterop(2, 2, (a, b) => a + b);

As a general rule, you should use natural F# functions when operating with other F# code, but when designing an API that will likely be called from other CLR languages, use Func<> or Action<> with those outside APIs.

Dealing with F# Discriminated Unions from C#

Discriminated unions, from the standpoint of the C# programmer, have the appearance of Enums, but do not quite match up with them. As a result of this, it is not terribly common for F# programmers to expose discriminated unions in APIs. However, if an API returns such a discriminated union, it is helpful to have a sense of what will happen. Consider the following:

type CoolColors = Green | Blue | Purple

type FSharpLibrary() =
  member this.ReturnADiscriminatedUnion() =
    Blue

The preceding code is simple F# code that returns CoolColors.Blue, which is part of the CoolColors discriminated union. The following is perfectly good C# code that utilizes this:

var shouldBeBlue = someFSharpObject.ReturnADiscriminatedUnion();
var shouldBeTrue = shouldBeBlue == CoolColors.Blue;

As can be seen, it is almost like working with an enumeration. However, unlike an enumeration, there is no ability to use the discriminated union in a switch statement, or do anything nearly as powerful as an F# match construct. One of the reasons discriminated unions are not recommended for use in APIs is that they tend to cause a number of ugly constructs in languages that are not suited for using them, such as:

if (shouldBeBlue == CoolColors.Blue)
{
  //do blue stuff
}
else if (shouldBeBlue == CoolColors.Green)
{
  //do green stuff
}
else if (shouldBeBlue == CoolColors.Plaid)
{
  //do crazy stuff
}
                                            
Dealing with F# Discriminated Unions from C#

The preceding example is exactly the kind of C# code that most programmers want to get away from, especially given that anything that is making a decision like that in C# code based on a discriminated union could probably do so with much more clarity inside the F# library. Although it may be necessary in a case where you deal with third-party libraries, it is generally better to deal with discriminated unions in the world of F# rather than in C#.

Working with F# Option Types from C#

F# option types are another one of those cases that may sometimes leak outside of the F# world but probably should remain in the F# world at least until there is a clean mapping in the CLR from F# to the rest of the CLR languages. Consider an API written in F# containing something like the following that uses an option int as a return value:

member this.ReturnAnOption() =
  if DateTime.Now.Hour = 6 then
    Some(6)
  else
    None

The poor C# user who uses this API is going to need to not only reference FSharp.Core (which has the type definition for options), but will also need to do something like the following to convert the option into a more appropriate type for C#:

//getting back an option
var mightHaveSomething = someFSharpObject.ReturnAnOption();
var someNullable =
  mightHaveSomething == FSharpOption<int>.None
  ? new int?()
  : mightHaveSomething.Value;

Of course, there are some key differences between Nullable and F# options. Nullable applies only to structs, whereas FSharpOption applies to any kind of object. The FSharpOption is also more explicit in that it can differentiate between a null object and None — though truth be told, in practice, most of the time null and None have the same semantic meaning from the perspective of the C# programmer.

RULES OF THUMB FOR WRITING F# APIS

In this chapter so far, various means of dealing with what C# programmers might call "F# oddities" have been covered. Over time, it is likely that many of these concepts from F# that are not easily expressed in the CLR will find their way in, much like Tuple<T> has become a core part of the .NET framework. However, until something is in that core, it is far safer to write APIs that conform to things that map easily to other languages as they are currently defined.

To help write APIs that will interoperate nicely with other languages, consider the following rules of thumb:

  • Avoid using Tuple as return types from APIs. Even though the concepts are supported in C# and other CLR languages in .NET 4.0, code that works with Tuple<T> is often hard to read outside the context where the Tuple is defined.

  • Avoid FSharpOption unless you are confident your API consumer can take a dependency on FSharp.Core. The FSharpOption<T> type is, arguably, a better Nullable than Nullable, something that can help C# programmers move away from using null as a sentinel value meaning None. However, the drawback of forcing the direct dependency to FSharp.Core is an important consideration, as is the complexity of dealing with a legacy codebase where Nullable and FSharpOption might end up getting mixed together, creating even more confusion than you started with.

  • Record types are all good — use them at will. F# Record types allow a programmer to do in one line of F# code what often takes many lines of C#, expressing the concept of an immutable holder of data (that is, for transfer between systems) very cleanly.

  • Avoid use of discriminated unions in public APIs. These are great for internal F# code, but to C# code, the language constructs for dealing with them in a way idiomatic to good C# simply are not present.

  • Do not use F# native functions as parameter types in public APIs. Prefer Func<> and Action<> over native F# functions in APIs that pass functions around, as there is a great deal of pain involved for languages other than F# to create proper F# functional constructs.

  • Use each language for its purpose. For the same reason C# is likely better than F# for expressing hardcore OO concepts, F# is better than C# for doing things like functional composition. Within those areas of strength, it is best to stick with the language at hand. Don't try to use F# to compose functions written in C#, and don't try to use F# in some elaborate C# inheritance chain. In an API where one interoperates with the other, select concepts that are cleanly expressed in both languages when working over the seam.

SUMMARY

There are great reasons for being a polyglot — that is, proficient in multiple languages in a manner that allows the programmer to use each language where it is best suited. Many enterprise solutions have aspects that are best expressed in functions, such as business intelligence and complex calculations, where other aspects are best expressed in objects. By knowing not only how to use both languages, but also how to use them together, the .NET programmer is well suited to not only get the best of both worlds, but also to do so in a way that does not make both F# and C# code bases difficult to maintain.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset