This might not be the most efficient or flexible parsing method, but that’s not really the point of the current exercise and it’s very clear what it’s doing.
Next, we want to create a quotation that represents a curried function based on our format type. This is where I needed Tomas’ help – it turns out there isn’t any easy way to do this with the <@@ ... @@> syntax I’ve usually used to build quotations for type providers.
Tomas reminded me that the Microsoft.FSharp.Quotations namespace gives direct access to the underlying classes that represent the expression tree of the quotation. This allows us to build an expression tree recusively; check out Tomas’ explanation of the technique for more details of how it works.
That’s the hard stuff out of the way! Now we just have some type provider boiler plate. We’re going to provide a type provider named TPrint which takes a single parameter (our format string). Once the parameter is supplied, we provide a single static property which is an FSharpFunc type which matches the signature required by the format string.
So, put it all together and you get a type provider which allows you to do this:
TPrint<"A %s string! %s %d">.show"hello""world"32// val it : string = "A hello string! world 32"TPrint<"Number one: %d! Number two: %d! A string: %s!">.show12"My string!"// val it : string = "Number one: 1! Number two: 2! A string: My string!!"
So; nothing there that the built in printf doesn’t already do for you. But, this does start opening up some options for providing much more idiomatic F# style APIs then I’ve really seen so far from Type Providers, which tend to provide very OO style interfaces. Should be some interesting ideas in there to explore!