For a while now, I’ve held the opinion that the “sweet spot” for functional languages on the JVM and CLR will be in the services space, since services and functions seem pretty similar to one another in spirit—a given input produces a given output, with (ideally) no shared state, high concurrency expectations, idempotent processing, and so on. This isn’t to say that a functional language is going to make a non-trivial service trivial, but I think it will make it simpler and more likely to scale better over time, particularly as the service gets more complicated.
As part those explorations into the union of services and functional languages, I’ve been taking some of Michele Leroux Bustamente’s excellent labs from Learning WCF and flipping the services over to F#. Along the way, I’ve discovered a few “quirks” of F# that make building a WCF service a tad more complicated than it needs to be, so I’ve decided to blog what’s going on so others can find it easier.
(Many thanks to Nick Holmes’ blog, which helped identify one of the first problems I ran into, though a few things have changed since he blogged back in February, so I thought I’d catch everything up to the Sep 08 CTP of F#.)
This isn’t intended to be a tutorial on WCF, so if you’re not familiar with WCF, I strongly suggest you go get Michele’s book. I’m assuming you’ll know the WCF basics (address, binding, contract, config files, behaviors, etc), and I just want to show the deltas necessary to make F# work. Note that I’m just doing the service side of things—I believe clients will probably continue to be written in C# or VB or some other OO language, in keeping with the theory that OO will remain the predominant way of developing client-facing stuff. (Note that this also neatly avoids the basic problem that svcutil.exe only generates either C# or VB proxy code, and that “Add Service Reference” isn’t available inside an F# project, as of this writing.)
Defining Contracts in F#
The first step in any straight-up WCF service is, of course, to define the contract that both sides will agree to. (Yes, I know, we could do everything in terms of picking Message types apart; I’ll get to that in a later piece.) First things first: taking Michele’s HelloIndigo_Part1 solution, I add a new project to it, “FHost”, an F# application. Add the System.ServiceModel and System.Runtime.Serialization assemblies, and we’re good to get going.
Michele’s “HelloIndigo_Part1” solution defines the contract between client and service this way:
1: namespace Host
3: [ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
4: public interface IHelloIndigoService
7: string HelloIndigo();
9: // ...
This contract can be consumed in two ways; one is to build this interface into its own assembly that’s linked to both the WCF service host and to the WCF client, but in her example (as is perfectly reasonable in a WCF project), she repeats the interface in both the client and the service, so to be faithful to that, let’s define the interface in the F# code:
3: open System
4: open System.ServiceModel
6: [< ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06") >]
7: type IHelloIndigoService =
8: [< OperationContract >]
9: abstract HelloIndigo: unit -> string
(The color syntax highlighting is off because I’m using the C# mode of the “Code Snippet” plugin in Windows Live Writer to post the code, and it doesn’t have an F# mode. Yet.)
Pay very close attention to the interface definition in F#, because there is a subtle WCF “bug” that F# exposes by accident. When F# compiles an interface, if a method in the interface has parameters, if no name is specified for that parameter, then WCF will throw an ArgumentNullException when you try to run svcutil.exe over the compiled assembly, or when you pass the type in to the ServiceHost constructor, claiming “Value cannot be null. Parameter name: name”. The problem is that F#, unlike C# or VB, allows methods to have parameters without names, and WCF can’t handle this. Verifying this is a b*tch; if you use ILDasm to view the F#-compiled assembly, it looks like there are parameter names there, because ILDasm generates them as placeholders for display. (Reflector is your friend here.)
The WCF team has basically said that this behavior is by design—SOAP, which is a key concept to the WCF stack, doesn’t really have great support for unnamed parameters (and yes, I know, this is not exactly true, but I’m not going to get into that debate here), so the WCF team has basically said there’s really nothing they can do but maybe issue a better error message than ArgumentNullException.
Lesson #1: Always name your WCF contract interface params.
Defining the Service Implementation
Next step is to define the service implementation. Again, Michele’s code looks like so:
1: public class HelloIndigoService : IHelloIndigoService
3: public string HelloIndigo()
5: return "Hello Indigo";
Not a really difficult operation, so converting that to F# is pretty straightforward:
1: [< ServiceBehavior(ConfigurationName="HIS") >]
2: type HelloIndigoService() =
3: interface IHelloIndigoService with
4: member s.HelloIndigo() : string =
5: "Hello Indigo"
There are two things important to this definition. First, the parentheses at the end of the “type” declaration line create a default no-argument constructor for the HelloIndigoService, which is required—without it, WCF is going to complain about being unable to construct an instance of this type.
Lesson #2: Always provide the default type constructor in the service implementation.
Second, the ServiceBehavior attribute is one I’ve added, because F# does some funky things with the type names during compilation; for example, since my F# code is in a file called “Host.fs”, the F# compiler synthesizes a class called “Host” which acts as a nesting wrapper around everything else in the file, so technically the typename of HelloIndigoService is now “Host+HelloIndigoService”, which will cause some chaos when WCF tries to match up the service name with the appropriate entry in the App.config file. You can either make sure the App.config matches the CLR-level type names generated by the F# compiler, or you can explicitly specify the configuration names; I choose the latter, so that it’s a bit more clear what’s going on.
Lesson #3: Always specify the configuration name on the service implementation.
The App.config file, by the way, now looks like this, the only change from Michele’s labs being the changes to the configuration name of the service behavior (line 13):
1: <?xml version="1.0" encoding="utf-8" ?>
6: <behavior name="serviceBehavior">
7: <serviceMetadata httpGetEnabled="false" />
12: <service behaviorConfiguration="serviceBehavior"
14: <clear />
15: <endpoint address="HelloIndigoService"
18: contract="Host+IHelloIndigoService" />
19: <endpoint binding="mexHttpBinding"
21: contract="IMetadataExchange" />
24: <add baseAddress="http://localhost:8000/HelloIndigo" />
Still with me? One last part to go, defining the (self-hosting) host.
Defining the Self-Hosting Host
In simple examples, frequently the service code self-hosts, meaning it doesn’ t need to be deployed into IIS. Michele uses a wrapper class to defer some of the hosting details, a la:
1: internal class MyServiceHost
3: internal static ServiceHost myServiceHost = null;
5: internal static void StartService()
7: // Instantiate new ServiceHost
8: myServiceHost = new ServiceHost(typeof(HelloIndigoService));
12: internal static void StopService()
14: // Call StopService from your shutdown logic (i.e. dispose method)
15: if (myServiceHost.State != CommunicationState.Closed)
20: class Program
22: static void Main(string args)
27: Console.WriteLine("Press <ENTER> to terminate the host application");
I don’t quite think the wrapper is necessary, so I simplified it down to:
1: let main() =
2: Console.WriteLine("IHelloIndigoService = " + typeof<IHelloIndigoService>.ToString() )
4: let hisType = typeof<HelloIndigoService>
5: let host = new ServiceHost(hisType, ([| |] : Uri ) )
7: Console.WriteLine("Press <ENTER> to terminate the host application")
8: Console.ReadLine() |> ignore
One quirk of the current (Sept 08) F# CTP is that when working with variable-argument parameters (like the second argument of the ServiceHost constructor), F# doesn’t have a great syntax. We have to explicitly specify, in this case, an empty array of Uri objects, but simply specifying an empty array (“[| |]”) will be interpreted as an empty array of objects, and thus generate a compile error. We have to explicitly set the type of the array to be an array of Uri, hence the type specifier.
Oh, and don’t forget, if you’re running as a non-Administrator on Vista or XP, you’ll need to create a URL ACL to allow a non-Administrator user to create an HTTP endpoint; the relevant command for the example above is this:
netsh http add urlacl url=http://+:8000/HelloIndigo user=devtop-t42p\ted
(Obviously, you substitute in your own domain and username for mine.) Make sure to do this from an Administrator-enabled command prompt, or you’ll just get another security error.
The beautiful thing about this example is that if it works, you can use the Client written in C# without a hitch of a problem, thus demonstrating quite clearly that WCF isn’t sharing assemblies between client and service. Given that this service also sets up the MEX endpoint, you should also be able to run svcutil against the running service and generate proxy code if you want to prove that it’s doable; I didn’t do it for this example, since I trust that the App.config-specified MEX endpoint will still be there, and because I was more interested in taking the existing Client and making it work as-is.
More to come, but this should get you started, anyway. Thanks again to Michele for letting me scaffold off of her!