08 April 2015

F# dependency wrangling, interop, nancy

Defining dependencies, C# vs F#

One of the advantages of using F# is the ability to define things as functions rather than extra types. For instance, let's say I wanted to define a dependency (I like the term resource) that generates an ID.

In C#

I would likely do something like this:
 public interface IGenerateIds  
 {  
   string GenerateId(string entityPrefix);  
 }  
Then I would have to make different concrete implementations for testing, and also a production one:
 public class TestGeneratorOf1 : IGenerateIds  
 {  
   public string GenerateId(string entityPrefix)  
     { return "1";}  
 }  
 // you are testing this right?  
 public class TestGeneratorFails : IGenerateIds  
 {  
   public string GenerateId(string entityPrefix)  
   { throw new Exception("died"); }  
 }  
 public class RealGenerator : IGenerateIds  
 {  
   public string GenerateId(string entityPrefix)  
   { /* real implementation */ }  
 }  

In F#

I can directly define function signatures (called a type abbreviation):
 type GenerateId = string -> string  
This says given a string, return a string. This is a bit obtuse, so I can actually use aliases to give my signature more meaning when I look at it later:
 // aliases  
 type EntityPrefix = string  
 type CreatedId = string  
 // same signature, string -> string, but more descriptive  
 type GenerateId = EntityPrefix -> CreatedId  
This works a treat, and later I can easily define functions that implement this signature for whatever I need to test:
 let generateId1 prefix = "1"  
 let generateFail prefix : string = failwith "died"  
 let realGenerator prefix =  
   // real implementation  
The first two functions are so simple, they don't even need their own class file, and I will likely create them inline with the tests. The dependency signatures are small enough that they can all go in one centralized file and still be easily understood.

Compare the cognitive and organizational burden of the number of files. C# has at least 4 files for every dependency: interface, test pass, test fail, real. F# really only needs 1 file per dependency for the real implementation.

In my estimation, the F# version has a lot better signal-to-noise than the traditional OO dependency management!


That's great and all, but then there's interop...


So, I went to put this into practice... creating a REST endpoint using Nancy. Now Nancy is a brilliant framework, but it is quite OO-centric. Modules require inheriting a base class. DI requires interfaces and concrete classes. Fortunately, F# has these bits in it. You pretty much have to use the inheritance to use Nancy. But I wanted to continue to use functional dependencies. I also wanted to be able to unit test the endpoint with its dependencies in various conditions (good and bad) without using the real dependencies (i.e. database calls). But then have the option to deploy the same service with real dependencies and no change to the endpoint itself.

In Nancy, the way to do that is with a bootstrapper. I created bootstrappers for different dependency combinations that I wanted to test. These put the appropriate dependency in every request context.
 type IdGenGoodBootstrapper() =  
   inherit DefaultNancyBootstrapper()  
   override this.RequestStartup(container, pipelines, context) =  
      context.Items.["somekey"] <- box generateId1  

 type IdGenBadBootstrapper() =  
   inherit DefaultNancyBootstrapper()  
   override this.RequestStartup(container, pipelines, context) =  
      context.Items.["somekey"] <- box generateFail  
And I can setup my Nancy tests (nuget Nancy.Testing) to use those bootstrappers.
 let browser = new Browser(new IdGenGoodBootstrapper())  
Then my endpoint can pull the dependency function out of the request context...
 let gen = unbox<GenerateId> x.Context.Items.["somekey"]  
 // KABOOM  

... except that it crashes

It appears that the way F# works, these function signatures only work for the IDE. But at run time they compile to some core F# types (e.g. FSharpFunc). Not only that, but it appeared to me that they could be more generically typed than the defined abbreviation: e.g. a FSharpFunc<String, String> could be replaced with FSharpFunc<object, String> if you don't actually do something String-specific in the function. Combine that with the fact that at run-time, you can't get the real type definition of an abbreviated type (e.g. typedefof<GenerateId> will be a single FSharpFunc with no type parameters given). As a result, I found no way to pull a pure function back out of the dictionary and use it.

Coming from C#, this was completely unexpected to me. And I suppose it speaks to the very OO-ingrained nature of the CLR. However, just before posting a well-developed Stack Overflow question on the subject, I discovered a work-around.

Taking advantage of the OO nature, I can wrap the function signature in a record type (which ultimately gets emitted as a class, I think), and the run time can apparently cast that back to something that works.
 type GenerateId = { GenerateId: EntityPrefix -> CreatedId; }  

Then the code has to be changed to wrap the function in a record.

In the bootstrappers
 type IdGenGoodBootstrapper() =  
   inherit DefaultNancyBootstrapper()  
   override this.RequestStartup(container, pipelines, context) =  
      context.Items.["somekey"] <- box { GenerateId = generateId1 }

 type IdGenBadBootstrapper() =  
   inherit DefaultNancyBootstrapper()  
   override this.RequestStartup(container, pipelines, context) =  
      context.Items.["somekey"] <- box { GenerateId = generateFail }  
In the endpoint
 let gen = unbox<GenerateId> x.Context.Items.["somekey"]  
 let result = gen.GenerateId "asdf"  

And now it works!

Discovering this was quite the process. It was a bit of a letdown that function signatures are so limited at runtime, but the work-around is quite minimal. Overall, I am very happy at the way this works.

No comments: