Wednesday, September 15, 2010
Monday, June 14, 2010
WebSharper™: Towards Automated JavaScript Bindings
Cross-posted from the IntelliFactory blog.
This blog describes the pre-release WebSharper™ Extensibility Framework.
How would I call reasonably complex JavaScript functions from WebSharper™? For an example, consider jQuery.ajax.
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.
In practice, a big part of WebSharper™ codebase turns out to consist of stubs like these - classes and functions that instruct F# how to call a particular JavaScript API (think jQuery, Ext JS) and provide types for the API. The WebSharper™ JavaScript-binding code is repetitive, tedious to write and maintain and not straightforward to generate.
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:
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.Config
above 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<int> + T<string>
. 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>
Friday, June 11, 2010
Getting Started with PowerShell
In an attempt to get out of the Cygwin habbit, I have tried Windows PowerShell. It is definitely going in the right direction - not perfect yet, but worth a look.
First, download the Windows Management Framework.
Then, run powershell_ise
(on Vista+ - by right-clicking the icon and selecting to run as Administrator). powershell
is the shell itself, and runs in the usual command window. powershell_ise
is a graphical wrapper around it. The ISE is generally helpful. Note that the ISE fails to run interactive sessions, however, such as the F# interactive.
Next step: run the following in PowerShell, otherwise no scripts will run.
Set-ExecutionPolicy RemoteSigned
Then, you really should create a profile. I store mine in Dropbox, so that I can share it across all the computers:
$dir = split-path -parent $profile # poor-man's dirname
mkdir -f $dir
echo '. "\My Dropbox\powershell\profile.ps1"' > $dir/profile.ps1
Now editing profile.ps1
..
Designing a Core/Simplified JavaScript
I am working towards a core language for JavaScript... JavaScript by itself is nice for humans but not for compilers. The language definition has just one special case too many.
Here is a take on the Core:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
And then expressions are instances of Core Scheme expressions instantiated with Primitive
.
1 2 3 4 5 6 7 8 9 |
|
Notes:
No expression/statement distinction - this should be automated.
JavaScript calls are modelled with
Apply
where the first argument is alwaysthis
argument. Thereforefoo.bar(baz)
becomesApply (foo.[bar], foo :: baz)
.Global variable references are modelled as
Lookup
, saywindow
becomesLookup "window"
.LetRec
clause, as in Scheme, is derived - it is modelled withAssign
andLet
.Some JavaScript assignments are field assignments and not variable assignments - and these are a primitive, for example
foo.bar = 1
becomesSet (foo, bar, 1)
.No
try-finally
- can be reduced toTry
.No loops other than
While
- others can be reduced to it.
This seems to be expressive enough. I will probably update the definition after trying to write a few optimizations.
Thursday, June 10, 2010
VisualStudio Xml Editor Completion
Problem:
Get autocompletion for Apache Ivy files in VisualStudio.
Solution:
<?xml version="1.0" encoding="utf-8"?>
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://doiop.com/ivy-2.1.0.xsd">
</ivy-module>
How:
Copied the schema shipping with Ivy to Public folder in Dropbox and shortened the URL.