fsharp/fsharpx — pull request #185 by ovatsus (2012-12-09 20:49:03+00:00)
Title: Record zipper type provider / poor man macros

This is work in progress and it's not meant to be pulled, but I think it's the best way to start a discussion

The idea is the following: How can we (ab)use the type provider mechanism to be able to the same kind of transformations than we can do in lisp or clojure macros?
The concrete example I'm trying to build is to take a set of records and create a new set of records augmented with lens functionality (http://bugsquash.blogspot.co.uk/2011/11/lenses-in-f.html) but without having to define all the record fields with clunky Get and Set functions. (Actually in clojure we wouldn't need this as it's all maps and we can just pass a list of keys, but I'd like to be able to have that expressivety without loosing type safety). It would work by having a .fs with the definition of records, that we wouldn't include in the project, and then by using the type provider like this
type outerType = RecordZipperProvider<"file.fs"> we would get all the augmenter records nested inside outerType (The ideal here really would be to return a module - which is behind the scenes just a static class - but I don't think that would be possible)

Another example could be to add DataContractMember attributes to all record fields without having to pullute the definition with them

To be able to do this two steps are needed:

1) Get the original records into memory in a parsed way

2) Generate new records in a type provider

For 1 I've tried:
* the PowerPack's CodeDom - couldn't get it to work, and looking at the source it works spanning fsc.exe processes, which is very heavy
* Tweak @tpetricek FSharp.CodeFormat (https://github.com/tpetricek/FSharp.Formatting/tree/master/src/FSharp.CodeFormat )and call something else from the compiler insted of GetDataTipText - also couldn't get it to work
* Use the version of compiler with everything public and call CompileToDynamicAssembly - seemed promissing but it crashes on some resources related code inside the fsharp compiler
* Also use compiler but call the Compile method to generate the assembly to disk. Not as efficient as the previous one but I was able to get this to work and it's the current state. The big downside is that we leave garbage in the temp folder, because after calling Assembly.ReflectionOnlyLoad the dll is locked. One workaround might be to do this on a separate AppDomain and then unload it

2 Should be easy, just enumerate the types and generate new ones. But there's a problem. How can I generate a record in a type provider? I was able only to generate classes. And even if we can generate the classes with all the extra bells and whistles that make fsharp recognize it as a record, will type inference work correctly when creating values of those types?

Right now it's only compiling the file passed as argument and for each record/field it's generating a class/property. Anyone interested in helping me pursue this? It'll probably always be kind of hackish (unless F# 4 adds something that improves this, like being able to pass typeof<> as type provider parameters), but think it can be useful on some scenarios.