четверг, 17 июня 2010 г.

Playing with WebSharper

I’ve decided to make post about WebSharper long time ago…and finally time has come :).

The idea behind the WebSharper is not new, compilers that translate some source language to JavaScript already exists (for example GWT). Main distinguished feature of WS is using F# as a source language: built-in metaprogramming capabilities, type inference, succinct and expressive syntax, seamless integration with .NET platform makes it a very good choice both server (F# itself)and client side (translated JavaScript). Besides JavaScript translation WS provides statically-typed wrappers for existing JavaScript libraries (like JQuery), HTML combinators for defining content of pages, formlets and many other interesting things.

As a sample we’ll make simple StickyNotes application. Web design is not my primary and favorite skill, so I'll omit cross-browser compatibility and bind all styles to Firefox. For a start server-side part will be trivial: storing all notes data in application state. Later (in forthcoming posts) it will be extended with specific user notes, registration routine (with formlets), persisting data in database etc…

Server part

module State = 

type Note =
{ X : int
Y : int
Content : string }

let private key = "StickyNotesState"
let private doSave (notes : Note list) =
HttpContext.Current.Application.Set(key, notes)
notes

let private doLoad () =
match HttpContext.Current.Application.Get(key) with
| :? list<Note> as v -> v
| _ -> doSave []

let private lockObj = obj()

let save notes = lock lockObj (fun () -> doSave notes)
let load () = lock lockObj doLoad

Type Note stores basic note information (coordinates and content).Remaining part of the module just store/load functionality with HttpApplicationState on backend. This code has no WS specific features, just pure F#.

Client/Server communucations

module Rpc = 
[<Rpc>]
let loadNotes () =
State.load ()

[<Rpc>]
let saveNotes notes =
State.save notes

Client will make server calls througn invoking methods annotated with Rpc attribute.

Client side.

[<Require(typeof<Styles.StickyNotes>)>]
module Notes =

// client-side storage for notes
[<JavaScript>]
let notes = System.Collections.Generic.Dictionary<_, _>()

// configuration data for JQuery.animate function
type AnimateConfiguration = { opacity : float }

[<JavaScript>]
let main () =

// moves specified element to the top in z-order
let maxZ = ref 0
let bringToTop (e : Element) =
incr maxZ
e.Css("z-index", string (!maxZ))


let body = Div []

// create 'Note' visual component and append it to body
// if state is defined then it contains previousy stored state
let noteId = ref 0
let createNote (state : option<Note>) =
let currentId = !noteId
incr noteId

let edit = Div [ Class "edit"; Html5.Attr.ContentEditable "true"]
let close = Div [Class "closebutton"]
let rec note =
Div [Class "note"] -< [
Div [Class "header"] |>! OnMouseDown (fun _ _ -> bringToTop note)
close
edit
]

close |> OnClick(fun _ _ ->
note.JQuery.Animate({opacity=0.3}, 300.0, "linear", (fun () ->
notes.Remove(currentId) |> ignore
note.Remove()
)) |> ignore
)

// make element draggable
JQueryUI.Draggable.New(note, JQueryUI.DraggableConfiguration(Handle = ".header")) |> ignore
notes.Add(currentId, (note, edit))

match state with
| Some(n) ->
edit.Append n.Content
note.Css("left", string n.X + "px")
note.Css("top", string n.Y + "px")

| _ ->
()

body.Append(note)

// saves current snapshot of notes in server storage
let saveNotes (el : Element) (_ : JQueryEvent) =
el.SetAttribute("disabled", "true")
el.Text <- "Saving..."

notes
|> Seq.map(fun kv ->
let n,e = kv.Value
let pos = n.JQuery.Position()
{ X = pos.Left; Y = pos.Top; Content = e.Html }
)
|> Seq.toList
|> Rpc.saveNotes

el.Text <- "Save"
el.RemoveAttribute("disabled")


// restore previous state
let notes = Rpc.loadNotes()
for n in notes do
createNote (Some n)

Table [
TR [
TD [ Width "30"] -< [Button [Text "Create"] |>! OnClick(fun _ _ -> createNote None) ]
TD [ Button [Text "Save" ] |>! OnClick saveNotes ]
]
TR [TD [ColSpan "2" ] -< [body] ]
]


[<JavaScriptType>]
type Body() =
inherit Web.Control()
[<JavaScript>]
override this.Body = Notes.main ()

Notes:

  1. Javascript attribute marks items that should be compiled into JavaScript
  2. AnimateConfiguration type is static wrapper for calling .animate function. WebSharper JavaScript translator converts F# record types into JavaScript objects with matching fields. We need to pass fixed number of parameters so solution with record will be shorted than common approach from section 7.
  3. Element.Css function sets style property to given object via calling .css()
  4. Page structure is defined with handy HTML combinators(Div, Table etc…)
  5. -< combinator appends one sequence to another. It is basically used to create element both with attributes and child elements.
  6. |>! combinator allows attaching event handlers to elements in a composable way. Its definition is simple:
    let (|>!) x f = f x; x 
  7. JQueryUI.Draggable is a typed wrapper to  JQuery draggable plugin. It accepts parameters in form of DraggableConfiguration object: type with fields having DefaultValueAttibute attached. This is common convention for passing objects with optional fields to JavaScript code.
  8. When F# to JavaScript translator meets type annotated with JavaScriptTypeAttribute, it generates not only data fields but also class representation.

Also you’ve noticed Require attribute atop of Notes module. This attribute is utilized by WebSharper resource control system that tracks all necessary dependencies(css or js files) and orders them properly – all these activities are based on declarative information provided by developer. First of al you need to define a resource, in our sample it will be external css file.

module Styles = 
type StickyNotes() =
interface IResource with
member this.Render(r, w) =
let u = r.GetWebResourceUrl(typeof<StickyNotes>, "StickyNotes.css")
Resource.RenderCss u w

Resource is type that has default constructor and implements interface IResource.

After than annotate all types that depends on this resource with Require attribute (you can also apply RequireAssembly to assembly). WebSharper will build directed graph and use for resource management. ScriptManager control that should be embedded in the head section emits all necessary page resources preserving correct order.

Entire VS2010 solution with this sample can be found here, it already contains WebSharper JQueryUI extension but you also need WebSharper to be installed, so you can build and run the application.

Demonstration:

1. I’ve opened Firefox and created two notes

initial

2. One note is closed (it’s a pity, but I wasn’t able to capture fancy semi-transparent note when it dissapears)

first_deleted

3. I saved the state and opened the same page in Chrome (on the right).

same_page_in_chrome

Stay tuned!

вторник, 8 июня 2010 г.

F# Performance of events (update)

There is another solution to the challenge we’ve met last time. In my previous post I’ve skipped it because it is F# specific and result cannot be used directly in other languages. However after discussion with ControlFlow I think this solution is also worth mentioning.

As you remember the problem was inability to call Invoke method of the delegate. Using statically resolved type parameters and member constraints we can make compiler do all the job for ensuring that type has method Invoke with particular signature and calling it properly.

type MCEvent< ^D, ^A when ^D :> Delegate and ^D : delegate< ^A, unit> and ^D : (member Invoke : obj * ^A -> unit) and ^D : null>() = 
[<DefaultValue>]
val mutable multicast : ^D

member inline this.Trigger(sender : obj, arg : ^A) =
match this.multicast with
| null -> ()
| d -> (^D : (member Invoke : obj * ^A -> unit)(this.multicast, sender, arg))

member inline this.Publish =
{ new IDelegateEvent< ^D> with
member x.AddHandler(d) =
this.multicast <- System.Delegate.Combine(this.multicast, d) :?> ^D
member x.RemoveHandler(d) =
this.multicast <- System.Delegate.Remove(this.multicast, d) :?> ^D }

//test helper
type MCEventClass(num) =
let event = new MCEvent<EventHandler<EventArgs>, _>()

[<CLIEvent>]
member this.Event = event.Publish

member this.Run () =
for i in 1 .. num do
event.Trigger(this, new System.EventArgs())


Test class is appeared to be almost the same as ones we’ve used in previous post. However if you open compiled assembly with Reflector you’ll see the difference

// FsFastEventClass
public void Run()
{
int i = 1;
int num = this.num;
if (num >= i)
{
do
{
this.@event.Trigger(this, EventArgs.Empty);
i++;
}
while (i != (num + 1));
}
}

// MCEventClass
public void Run()
{
int i = 1;
int num = this.num;
if (num >= i)
{
do
{
MCEvent<EventHandler<EventArgs>, EventArgs> event2 = this.@event;
object sender = this;
EventArgs e = new EventArgs();
if (event2.multicast != null)
{
event2.multicast(sender, e);
}
i++;
}
while (i != (num + 1));
}
}

As you see compiler have inlined code of Trigger method inside Run  and accessed field multicast directly. That’s why we replaced let binding with val.

namespace Benchmark
{
class Program
{
const int Iters = 1000000;

static void Run(string caption, Action action)
{
Console.WriteLine("{0} started", caption);
var sw = Stopwatch.StartNew();
action();
sw.Stop();
Console.WriteLine("{0}:{1}", caption, sw.Elapsed);
}


static void Main(string[] args)
{
Run("F# events", RunFSEventTest);
Run("Fast events", RunFastEventTest);

// initial pass to trigger generation of invoker (so generation time is not included in tests)
RunEventV2Test(1);
Run("Precomputed events", () => RunEventV2Test(Iters));
Run("MemberConstrainedEvents", RunMemberConstrainedEventTest);
}

private static void RunFSEventTest()
{
var fs = new Events.FsEventClass(Iters);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

private static void RunFastEventTest()
{
var fs = new Events.FsFastEventClass(Iters);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

private static void RunEventV2Test(int n)
{
var fs = new Events.GenFastEventClass(n);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}
private static void RunMemberConstrainedEventTest()
{
var fs = new Events.MCEventClass(Iters);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

/*
F# events started
F# events:00:00:8.8833742
Fast events started
Fast events:00:00:00.0300628
Precomputed events started
Precomputed events:00:00:00.876707
MemberConstrainedEvents started
MemberConstrainedEvents:00:00:00.0251707
*/
}
}

суббота, 5 июня 2010 г.

F# Performance of events

Events in F# are first class citizens, and this is in fact feature with vast amount of applications. We can compose events using combinators, pass events to functions, return them as a results… IDelegateEvent wrap delegates and under the hood use Delegate.DynamicInvoke for invoking methods bound to delegate, causing significant performance degradation. Rick Minerich has made good description of this problem and also suggested solution that has brilliant performance characteristics but binds event to particular delegate type.

So, is is possible to have both possibility to use arbitraty delegates and good performance characteristics? Linq expression trees come to the rescue. We cannot access Invoke method of delegate directly but we can create typed helper that will do this instead of us.

// helper type that will perform invocation
type Invoker<'D, 'A> = delegate of 'D * obj * 'A -> unit

// :)
type EventV2<'D, 'A when 'D :> Delegate and 'D : delegate<'A, unit> and 'D : null>() =
static let invoker =
let d = Expression.Parameter(typeof<'D>, "dlg")
let sender = Expression.Parameter(typeof<obj>, "sender")
let arg = Expression.Parameter(typeof<'A>, "arg")
let lambda = Expression.Lambda<Invoker<'D, 'A>>(Expression.Invoke(d, sender, arg), d, sender, arg)
lambda.Compile()

let mutable multicast : 'D = null

member x.Trigger(sender:obj,args: 'A) =
match multicast with
| null -> ()
| d -> invoker.Invoke(d, sender, args) // DelegateEvent used: d.DynamicInvoke(args) |> ignore

member x.Publish =
{ new IDelegateEvent<'D> with
member x.AddHandler(d) =
multicast <- System.Delegate.Combine(multicast, d) :?> 'D
member x.RemoveHandler(d) =
multicast <- System.Delegate.Remove(multicast, d) :?> 'D }

// helper to be used in test
type GenFastEventClass(num) =
let event = new EventV2<EventHandler<EventArgs>, _>()

[<CLIEvent>]
member this.Event = event.Publish

member this.Run () =
for i in 1 .. num do
event.Trigger(this, new System.EventArgs())

Performance measurements (I took code for other tests from Rick’s post):

class Program
{
const int Iters = 1000000;

static void Run(string caption, Action action)
{
Console.WriteLine("{0} started", caption);
var sw = Stopwatch.StartNew();
action();
sw.Stop();
Console.WriteLine("{0}:{1}", caption, sw.Elapsed);
}


static void Main(string[] args)
{
Run("F# events", RunFSEventTest);
Run("Fast events", RunFastEventTest);

// initial pass to trigger generation of invoker (so generation time is not included in tests)
RunEventV2Test(1);
Run("Precomputed events", () => RunEventV2Test(Iters));
}

private static void RunFSEventTest()
{
var fs = new Events.FsEventClass(Iters);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

private static void RunFastEventTest()
{
var fs = new Events.FsFastEventClass(Iters);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

private static void RunEventV2Test(int n)
{
var fs = new Events.GenFastEventClass(n);
int fsCalled = 0;
fs.Event += (s, a) => fsCalled++;
fs.Run();
}

/*
F# events started
F# events:00:00:08.4989916
Fast events started
Fast events:00:00:00.0316716
Precomputed events started
Precomputed events:00:00:00.0821696
*/
}

As you see the results of events with pregenerated indirect caller is very close to fast events, but we preserve ability to use arbitraty delegate. Cool, isn't it?

 
GeekySpeaky: Submit Your Site!