Wednesday, March 14, 2012

My failure, my experience (Write unit-test type provider)

Some people say the experience is from your failure. They could be wrong in some cases, but it fits my situation. If you read this and gain some experience when writing F# type provider, as a F# team member, I will be very happy. First of all, you have to download the project which can work with VS2011 Beta.

When I realize can write unit test in F#, I was thrilled and wanted to make the syntax more concise as my fingers was strangled when I typed F# attribute on my laptop keyboard. It was long time since I wrote the RegEx type provider, so I jumped onto it before thinking it through. This is the first and fatal mistake for this project. Let me explain it later.

I started to write the type provider interface by taking a DLL file name: (in the test.fsx)

[ < Literal > ]
let tesDll = @"\CodeLibrary.dll"
open Samples.FSharp.UnitTestTypeProvider
type T = UnitTestTyped< tesDll >
Inside the type provider, I can user reflection to get all the types and public methods to generate a wrapper. Everything seems simple and straightforward until I found out the methodInfo cannot be referenced in the invoke code in UnitTestProvider.fs line 76. Instead, it needs a quotation passed into the invoke code.

If this project is a failure, the experience I gain is to use quotation in type provider writing. The following is the way to write quotation and pass it into the invoke code section.

let l = if hasInputs then args.Tail.Tail.Tail else []
let callExpr = Quotations.Expr.Call(args.[1], mi, l )
let (Quotations.Patterns.Call(_,eq,_)) = <@ "aa"="b" @>
let eq = eq.GetGenericMethodDefinition().MakeGenericMethod(mi.ReturnType)
let eqTestExpr = Quotations.Expr.Call(eq, [callExpr; args.[2]])
<@@  if not %%eqTestExpr then failwith "not equal" @@>)

The above code get an equal operator and turn it into a generic method. The final equal operation is created by "MakeGenericMethod" with methodInfo's return type. In this way, we can do a strong type comparison and the performance is better. Everything works perfectly until I realize the erased type provider does not generate a concrete type and cannot insert customized attribute. This is the final blow to this project, as if there is no attribute, all my work is in vain. And also the erased type provider does not have a concrete type which cannot inherit, which seals the fate of this project. Now my project is doomed. :-(

The lesson I learnt is

  • Think the type provider interface first and think it through before started.
  • All the type provider we write with Beta is erased type provider, it does not support attribute decorated on the method
  • Erased type provider is not a concrete type, so inheritance won't work here.

  • quotation is the way to write invoke code. This sample only shows how to make Equal method working in the invoke code, it is not feasible to know all the quotation transforms; however, it definitely opens a door to the new territory. 

No comments: