This blog describes the pre-release WebSharper Extensibility Framework.
A first attempt would look like this:
[<Inline "jQuery.ajax($settings)">] let ajax (settings: obj) : obj = null
This is hardly acceptable as it throws away all the information present on the documentation page and makes it the user's responsibility to figure out what exactly the ajax function expects. But spelling out the precise types requires a lot more work.
At IntelliFactory we have been exprimenting with several approaches to the problem, culminating in something we currently call the Extensibility Framework, or EF for short. The basic idea behind EF is that instead of writing the bindings by hand, we use an F#-embedded DSL to describe them.
jQuery.ajax with EF might look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
let private AjaxConfig = Pattern.Config "AjaxConfig"  [ "async" =~ T<bool> "beforeSend" =~ J.XMLHttpRequest ^-> T<unit> "cache" =~ T<bool> "complete" =~ J.XMLHttpRequest * T<string> ^-> T<unit> "contentType" =~ T<string> "context" =~ T<obj> // .. ] let private JQueryClass = Code.Class "JQuery" |=> JQuery |+> [ "jQuery.ajax" => AjaxConfig ^-> T<unit> "jQuery.ajaxSetup" => AjaxConfig ^-> T<unit> "jQuery.contains" => J.Element?container * J.Node?contained ^-> T<bool> // ...
The use of the function would then look like this:
jQuery.Ajax(AjaxConfig(Async = false, ...))
The good news are several:
- This is code-generating code - repetitive patterns can be abstracted over with plain functions in F#.
- If a particular framework provides good documentation, this documentation can be parsed into EF values and then amended by merging it with hand-coded EF values where needed. Bindings generation is then semi-automatic.
- Some common code-generating patterns will come with EF. Consider
Pattern.Configabove which generates a new configuration object class with the given required and optional fields.
- Overload generation is automated. In EF, the type for a parameter that accepts either an int or a string can be spelled as T + T. The framework then figures out the correct overloads.
There is nothing particularly blog-worthy in the implementation of EF. It is mostly about figuring out the right interface (the most simple and yet diffuclt task there is). I will mention just one implementation detail I am particularly fond of. It is the use of F# operator overloading to describe method and function types. For example, these equalities are made to hold:
T<int -> int -> int> = T<int> ^-> T<int> ^-> T<int> T<int * int -> int> = T<int> * T<int> ^-> T<int>