Thursday, January 20, 2011

A simple implementation of formlets in F#

I've been blogging lately about functors in F#, in order to explain applicative functors, all so I could explain formlets now. Formlets are an abstraction of HTML forms. Formlets are composable, reusable, type-safe (as much as the host language is type-safe anyway) and extensible. And they are described, of course, in terms of applicative functors.

The concept of formlets was originally developed by Ezra Cooper, Sam Lindley, Philip Wadler, and Jeremy Yallop, as part of their research for Links, a new programming language oriented to web applications.

Since its introduction in 2008, formlets proved to be a very popular concept, being ported to Haskell, Scheme (Racket), Common Lisp (Hunchentoot), Scala and JavaScript.

If you regularly read the news about F# you probably know about WebSharper, a cool web framework for F#, which has formlets as one of its many features. Being a commercial product, WebSharper has great documentation on how to use formlets, but they only briefly mention the underlying concepts on how they work.

Let's see a sample formlet in an ASP.NET MVC application:

open System
open System.Web
open System.Web.Mvc
open System.Collections.Generic

[<HandleError>]
type HomeController() =
    inherit Controller()

    let inputInt = lift int input
    let inputDate =
        puree (fun month day year -> DateTime(year,month,day))
        <*> text "Month: " *> inputInt
        <*> text "Day: " *> inputInt
        <*> text "Year: " *> inputInt
    let formlet = 
        puree (fun name order ship amount -> name,order,ship,amount)
        <*> text "Name: " *> input <* br
        <*> text "Order date: " *> inputDate <* br
        <*> text "Shipping date: " *> inputDate <* br
        <*> text "Qty: " *> inputInt <* br

    member x.Index() =
        x.ViewData.["Message"] <- "Welcome to ASP.NET MVC!"
        x.View(box <| render formlet) :> ActionResult
    member x.Register() =
        let env = fromNV x.Request.Form
        let name,orderDate,shippingDate,amount = run formlet env
        x.ViewData.["Name"] <- name
        x.ViewData.["Ordered"] <- orderDate
        x.ViewData.["Shipping"] <- shippingDate
        x.ViewData.["Amount"] <- amount
        x.View() :> ActionResult
        F# Web Snippets

Index.aspx:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%: ViewData["Message"] %></h2>
    <p>
        <% using (Html.BeginForm("Register", "Home")) { %>
            <%= Model%>
            <input type="submit" value="Submit" />
        <% } %>
    </p>
</asp:Content>

Register.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Register
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <%: ViewData["Ordered"] %>: <%: ViewData["Name"] %> has requested <%: ViewData["Amount"] %> items
to be delivered on <%: ViewData["Shipping"] %>

</asp:Content>

You can hover over any value on the F# snippets in this post and get a tooltip with types, thanks to F# Web Snippets.

If you read my previous articles on applicative functors you'll recognize the standard operations pure (or puree), <*> and lift.
Note how the final formlet is composed from more basic formlets: one that models date input, which in turn uses an formlet to input integers. They all rely on a primitive input formlet (of type string Formlet) that models an <input type="text"/>.
Also note that we didn't define the name attribute for any input; formlets generate them automatically and also match them when handling submitted values. Running the app shows a form like this:

form

First thing that should spring to your mind when seeing this is "boy, this is ugly", otherwise I'm not doing my job right :)

Let's see now, step by step, how formlets can be implemented. We start by defining the types. First, a simple model of XML to represent HTML:

type xml_item =
    | Text of string
    | Tag of string * (string*string) list * xml_item list  // name * attributes * children

We define an environment that will hold key-value pairs from submitted forms:

type Env = (string*string) list

And finally, the Formlet type:

type 'a Formlet = int -> (xml_item list * (Env -> 'a) * int)

Type signatures carry a lot of information, so let's take a moment to read this. It's a function that takes an int and returns a tuple of stuff. We'll see that the int it takes is used to generate the form element name. The tuple of stuff returned contains: an XML forest (representing HTML), a function we'll call collector, and an int that will be used to generate the next form element name. The collector is used to lookup the submitted value from the environment.

A formlet is an applicative functor, so let's define puree:

[<AutoOpen>]
module Formlet =
    let puree (v: 'a) : 'a Formlet = 
        fun i -> [], (fun _ -> v), i

It's an empty XML, a constant collector, and the same input i. Here's the definition of <*> :

let (<*>) (f: ('a -> 'b) Formlet) (a: 'a Formlet) : 'b Formlet =
        fun i -> 
            let x1,g,i = f i
            let x2,q,i = a i
            x1 @ x2, (fun env -> g env (q env)), i

Step by step: we apply f, since it holds an 'a -> 'b, the collector g collects a function 'a -> 'b. Then we apply a. We return the concatenation of XML forests, the composition of collectors, and the final name seed.

Now some standard functions for applicative functors:

let lift f a = puree f <*> a
    let lift2 f a b : _ Formlet = puree f <*> a <*> b
    let ( *>) f a : _ Formlet = lift2 (fun _ z -> z) f a
    let ( <*) f a : _ Formlet = lift2 (fun z _ -> z) f a

A couple of functions to lift pure markup and text:

let xml (x: xml_item list) : unit Formlet =
        fun i -> x, (fun _ -> ()), i
    let text (t: string) : unit Formlet =
        xml [Text t]

The tag function creates a tag that wraps a formlet:

let tag (name: string) (attr: (string*string) list) (f: 'a Formlet) : 'a Formlet =
        fun i ->
            let xml,env,i = f i
            [Tag(name, attr, xml)],env,i

nextName is responsible for generating form element names:

let nextName =
        fun i -> "input_" + i.ToString(), i+1

Note that it returns an incremented counter, to be used in the following name.
The input primitive:

let input : string Formlet =
        fun i ->
            let name,i = nextName i
            let collector = List.find (fun (k,_) -> k = name) >> snd
            let tag = Tag("input", ["name",name], [])
            [tag], collector, i

The input formlet shows how everything fits together. It generates a fresh name to be used both in the collector and the input tag. This is what keeps the rendered form element and its respective collected value in sync.

Here's a simple <br/> for formatting:

let br: unit Formlet = xml [Tag("br",[],[])]

The run function gets the collector of a formlet, which in turn will produce the collected value when applied to the environment:

let run (f: 'a Formlet) : Env -> 'a =
        let _,e,_ = f 0
        e

That zero is the seed for form element names.

We render a formlet by mapping it to a System.Xml.Linq.XDocument:

open System.Xml.Linq
    let render (f: 'a Formlet) : XDocument =
        let xml,_,_ = f 0
        let (!!) t = XName.op_Implicit t
        let xtext (s: string) = XText s :> XObject
        let xattr (name, value) = XAttribute(!!name, value) :> XObject
        let xattrs attr = List.map xattr attr
        let xelem name attr children = XElement(!!name, attr @ children) :> XObject
        let rec renderForest x =
            let render' =
                function
                | Text t -> xtext t
                | Tag(name, attr, children) -> xelem name (xattrs attr) (renderForest children)
            List.map render' x
        let root = xelem "div" [] (renderForest xml)
        XDocument root

A helper function to make an Env from a NameValueCollection (such as Request.Form):

open System.Collections.Specialized
    let fromNV (a: NameValueCollection) =
        a.AllKeys
        |> Seq.collect (fun k -> a.GetValues k |> Seq.map (fun v -> k,v))
        |> Seq.toList

Even though this was a bare bones implementation of formlets, I hope it served to illustrate how they work. This implementation was taken almost verbatim from the paper The Essence of Form Abstraction [PDF] by the original developers of Formlets mentioned at the beginning of this article. I kept it as simple and clear as possible, with many type annotations that weren't really necessary and intentionally cutting out features.

Full source code is here.

Next, we'll see how formlets can be factored to a composition of primitive applicatives.

Read this article in Serbo-Croatian (translated by Anja Skrba from Webhostinggeeks.com)

namespace Formlets
union case xml_item.Text: string -> xml_item
Multiple items
val string : 'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------

type string = System.String

Full name: Microsoft.FSharp.Core.string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
union case xml_item.Tag: string * (string * string) list * xml_item list -> xml_item
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>

  type: 'T list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<'T>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<'T>
  implements: System.Collections.IEnumerable
type xml_item =
  | Text of string
  | Tag of string * (string * string) list * xml_item list

Full name: Formlets.xml_item

  type: xml_item
  implements: System.IEquatable<xml_item>
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<xml_item>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
type Env = (string * string) list

Full name: Formlets.Env

  type: Env
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<string * string>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<string * string>
  implements: System.Collections.IEnumerable
type 'a Formlet = int -> xml_item list * (Env -> 'a) * int

Full name: Formlets.Formlet<_>
Multiple items
val int : 'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------

type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>

  type: int<'Measure>
  implements: System.IComparable
  implements: System.IConvertible
  implements: System.IFormattable
  implements: System.IComparable<int<'Measure>>
  implements: System.IEquatable<int<'Measure>>
  inherits: System.ValueType


--------------------

type int = int32

Full name: Microsoft.FSharp.Core.int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
type AutoOpenAttribute =
  class
    inherit System.Attribute
    new : unit -> AutoOpenAttribute
    new : path:string -> AutoOpenAttribute
    member Path : string
  end

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

  type: AutoOpenAttribute
  implements: System.Runtime.InteropServices._Attribute
  inherits: System.Attribute
val puree : 'a -> int -> xml_item list * (Env -> 'a) * int

Full name: Formlets.Formlet.puree
val v : 'a
val i : int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
val f : ('a -> 'b) Formlet
val a : 'a Formlet
val x1 : xml_item list

  type: xml_item list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<xml_item>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<xml_item>
  implements: System.Collections.IEnumerable
val g : (Env -> 'a -> 'b)
val x2 : xml_item list

  type: xml_item list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<xml_item>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<xml_item>
  implements: System.Collections.IEnumerable
val q : (Env -> 'a)
val env : Env

  type: Env
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<string * string>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<string * string>
  implements: System.Collections.IEnumerable
val lift : ('a -> 'b) -> 'a Formlet -> 'b Formlet

Full name: Formlets.Formlet.lift
val f : ('a -> 'b)
val lift2 : ('a -> 'b -> 'c) -> 'a Formlet -> 'b Formlet -> 'c Formlet

Full name: Formlets.Formlet.lift2
val f : ('a -> 'b -> 'c)
val b : 'b Formlet
val f : 'a Formlet
val a : 'b Formlet
val z : 'b
val z : 'a
val xml : xml_item list -> int -> xml_item list * (Env -> unit) * int

Full name: Formlets.Formlet.xml
val x : xml_item list

  type: xml_item list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<xml_item>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<xml_item>
  implements: System.Collections.IEnumerable
type unit = Unit

Full name: Microsoft.FSharp.Core.unit

  type: unit
  implements: System.IComparable
val text : string -> unit Formlet

Full name: Formlets.Formlet.text
val t : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
val tag : string -> (string * string) list -> 'a Formlet -> int -> xml_item list * (Env -> 'a) * int

Full name: Formlets.Formlet.tag
val name : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
val attr : (string * string) list

  type: (string * string) list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<string * string>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<string * string>
  implements: System.Collections.IEnumerable
val xml : xml_item list

  type: xml_item list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<xml_item>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<xml_item>
  implements: System.Collections.IEnumerable
val env : (Env -> 'a)
val nextName : int -> string * int

Full name: Formlets.Formlet.nextName
Multiple overloads
System.Object.ToString() : string
System.Int32.ToString(provider: System.IFormatProvider) : string
System.Int32.ToString(format: string) : string
System.Int32.ToString(format: string, provider: System.IFormatProvider) : string
val input : int -> xml_item list * (Env -> string) * int

Full name: Formlets.Formlet.input
val lookup : ((string * string) list -> string)
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------

type List<'T> =
  | ( [] )
  | ( :: ) of 'T * 'T list
  with
    interface System.Collections.IEnumerable
    interface System.Collections.Generic.IEnumerable<'T>
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    member Tail : 'T list
    static member Cons : head:'T * tail:'T list -> 'T list
    static member Empty : 'T list
  end

Full name: Microsoft.FSharp.Collections.List<_>

  type: List<'T>
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<'T>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<'T>
  implements: System.Collections.IEnumerable
val find : ('T -> bool) -> 'T list -> 'T

Full name: Microsoft.FSharp.Collections.List.find
val k : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
val snd : ('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val tag : xml_item

  type: xml_item
  implements: System.IEquatable<xml_item>
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<xml_item>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
val br : unit Formlet

Full name: Formlets.Formlet.br
val run : 'a Formlet -> (Env -> 'a)

Full name: Formlets.Formlet.run
val e : (Env -> 'a)
namespace System
namespace System.Xml
namespace System.Xml.Linq
val render : 'a Formlet -> XDocument

Full name: Formlets.Formlet.render
type XDocument =
  class
    inherit System.Xml.Linq.XContainer
    new : unit -> System.Xml.Linq.XDocument
    new : obj [] -> System.Xml.Linq.XDocument
    new : System.Xml.Linq.XDeclaration * obj [] -> System.Xml.Linq.XDocument
    new : System.Xml.Linq.XDocument -> System.Xml.Linq.XDocument
    member Declaration : System.Xml.Linq.XDeclaration with get, set
    member DocumentType : System.Xml.Linq.XDocumentType
    member NodeType : System.Xml.XmlNodeType
    member Root : System.Xml.Linq.XElement
    member Save : string -> unit
    member Save : System.IO.TextWriter -> unit
    member Save : System.Xml.XmlWriter -> unit
    member Save : string * System.Xml.Linq.SaveOptions -> unit
    member Save : System.IO.TextWriter * System.Xml.Linq.SaveOptions -> unit
    member WriteTo : System.Xml.XmlWriter -> unit
    static member Load : string -> System.Xml.Linq.XDocument
    static member Load : System.IO.TextReader -> System.Xml.Linq.XDocument
    static member Load : System.Xml.XmlReader -> System.Xml.Linq.XDocument
    static member Load : string * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XDocument
    static member Load : System.IO.TextReader * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XDocument
    static member Load : System.Xml.XmlReader * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XDocument
    static member Parse : string -> System.Xml.Linq.XDocument
    static member Parse : string * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XDocument
  end

Full name: System.Xml.Linq.XDocument

  type: XDocument
  implements: System.Xml.IXmlLineInfo
  inherits: XContainer
  inherits: XNode
  inherits: XObject
type XName =
  class
    member Equals : obj -> bool
    member GetHashCode : unit -> int
    member LocalName : string
    member Namespace : System.Xml.Linq.XNamespace
    member NamespaceName : string
    member ToString : unit -> string
    static member Get : string -> System.Xml.Linq.XName
    static member Get : string * string -> System.Xml.Linq.XName
  end

Full name: System.Xml.Linq.XName

  type: XName
  implements: System.IEquatable<XName>
  implements: System.Runtime.Serialization.ISerializable
XName.op_Implicit(expandedName: string) : XName
val xtext : (string -> XObject)
val s : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
type XText =
  class
    inherit System.Xml.Linq.XNode
    new : string -> System.Xml.Linq.XText
    new : System.Xml.Linq.XText -> System.Xml.Linq.XText
    member NodeType : System.Xml.XmlNodeType
    member Value : string with get, set
    member WriteTo : System.Xml.XmlWriter -> unit
  end

Full name: System.Xml.Linq.XText

  type: XText
  implements: System.Xml.IXmlLineInfo
  inherits: XNode
  inherits: XObject
type XObject =
  class
    member AddAnnotation : obj -> unit
    member Annotation<'T> : unit -> 'T
    member Annotation : System.Type -> obj
    member Annotations<'T> : unit -> System.Collections.Generic.IEnumerable<'T>
    member Annotations : System.Type -> System.Collections.Generic.IEnumerable<obj>
    member BaseUri : string
    member Document : System.Xml.Linq.XDocument
    member NodeType : System.Xml.XmlNodeType
    member Parent : System.Xml.Linq.XElement
    member RemoveAnnotations<'T> : unit -> unit
    member RemoveAnnotations : System.Type -> unit
  end

Full name: System.Xml.Linq.XObject

  type: XObject
  implements: System.Xml.IXmlLineInfo
val xattr : (string * 'b -> XObject)
val value : 'b
type XAttribute =
  class
    inherit System.Xml.Linq.XObject
    new : System.Xml.Linq.XName * obj -> System.Xml.Linq.XAttribute
    new : System.Xml.Linq.XAttribute -> System.Xml.Linq.XAttribute
    member IsNamespaceDeclaration : bool
    member Name : System.Xml.Linq.XName
    member NextAttribute : System.Xml.Linq.XAttribute
    member NodeType : System.Xml.XmlNodeType
    member PreviousAttribute : System.Xml.Linq.XAttribute
    member Remove : unit -> unit
    member SetValue : obj -> unit
    member ToString : unit -> string
    member Value : string with get, set
    static member EmptySequence : System.Collections.Generic.IEnumerable<System.Xml.Linq.XAttribute>
  end

Full name: System.Xml.Linq.XAttribute

  type: XAttribute
  implements: System.Xml.IXmlLineInfo
  inherits: XObject
val xattrs : ((string * 'b) list -> XObject list)
val attr : (string * 'b) list

  type: (string * 'b) list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<string * 'b>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<string * 'b>
  implements: System.Collections.IEnumerable
val map : ('T -> 'U) -> 'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
val xelem : (string -> 'b list -> 'b list -> XObject)
val attr : 'b list

  type: 'b list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<'b>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<'b>
  implements: System.Collections.IEnumerable
val children : 'b list

  type: 'b list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<'b>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<'b>
  implements: System.Collections.IEnumerable
type XElement =
  class
    inherit System.Xml.Linq.XContainer
    new : System.Xml.Linq.XName -> System.Xml.Linq.XElement
    new : System.Xml.Linq.XName * obj -> System.Xml.Linq.XElement
    new : System.Xml.Linq.XName * obj [] -> System.Xml.Linq.XElement
    new : System.Xml.Linq.XElement -> System.Xml.Linq.XElement
    new : System.Xml.Linq.XStreamingElement -> System.Xml.Linq.XElement
    member AncestorsAndSelf : unit -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>
    member AncestorsAndSelf : System.Xml.Linq.XName -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>
    member Attribute : System.Xml.Linq.XName -> System.Xml.Linq.XAttribute
    member Attributes : unit -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XAttribute>
    member Attributes : System.Xml.Linq.XName -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XAttribute>
    member DescendantNodesAndSelf : unit -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XNode>
    member DescendantsAndSelf : unit -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>
    member DescendantsAndSelf : System.Xml.Linq.XName -> System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>
    member FirstAttribute : System.Xml.Linq.XAttribute
    member GetDefaultNamespace : unit -> System.Xml.Linq.XNamespace
    member GetNamespaceOfPrefix : string -> System.Xml.Linq.XNamespace
    member GetPrefixOfNamespace : System.Xml.Linq.XNamespace -> string
    member HasAttributes : bool
    member HasElements : bool
    member IsEmpty : bool
    member LastAttribute : System.Xml.Linq.XAttribute
    member Name : System.Xml.Linq.XName with get, set
    member NodeType : System.Xml.XmlNodeType
    member RemoveAll : unit -> unit
    member RemoveAttributes : unit -> unit
    member ReplaceAll : obj -> unit
    member ReplaceAll : obj [] -> unit
    member ReplaceAttributes : obj -> unit
    member ReplaceAttributes : obj [] -> unit
    member Save : string -> unit
    member Save : System.IO.TextWriter -> unit
    member Save : System.Xml.XmlWriter -> unit
    member Save : string * System.Xml.Linq.SaveOptions -> unit
    member Save : System.IO.TextWriter * System.Xml.Linq.SaveOptions -> unit
    member SetAttributeValue : System.Xml.Linq.XName * obj -> unit
    member SetElementValue : System.Xml.Linq.XName * obj -> unit
    member SetValue : obj -> unit
    member Value : string with get, set
    member WriteTo : System.Xml.XmlWriter -> unit
    static member EmptySequence : System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>
    static member Load : string -> System.Xml.Linq.XElement
    static member Load : System.IO.TextReader -> System.Xml.Linq.XElement
    static member Load : System.Xml.XmlReader -> System.Xml.Linq.XElement
    static member Load : string * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XElement
    static member Load : System.IO.TextReader * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XElement
    static member Load : System.Xml.XmlReader * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XElement
    static member Parse : string -> System.Xml.Linq.XElement
    static member Parse : string * System.Xml.Linq.LoadOptions -> System.Xml.Linq.XElement
  end

Full name: System.Xml.Linq.XElement

  type: XElement
  implements: System.Xml.IXmlLineInfo
  implements: System.Xml.Serialization.IXmlSerializable
  inherits: XContainer
  inherits: XNode
  inherits: XObject
val renderForest : (xml_item list -> XObject list)
val render' : (xml_item -> XObject)
val children : xml_item list

  type: xml_item list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<xml_item>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<xml_item>
  implements: System.Collections.IEnumerable
val root : XObject

  type: XObject
  implements: System.Xml.IXmlLineInfo
namespace System.Collections
namespace System.Collections.Specialized
val fromNV : NameValueCollection -> (string * string) list

Full name: Formlets.Formlet.fromNV
val a : NameValueCollection

  type: NameValueCollection
  implements: System.Collections.ICollection
  implements: System.Collections.IEnumerable
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.Serialization.IDeserializationCallback
  inherits: NameObjectCollectionBase
type NameValueCollection =
  class
    inherit System.Collections.Specialized.NameObjectCollectionBase
    new : unit -> System.Collections.Specialized.NameValueCollection
    new : System.Collections.Specialized.NameValueCollection -> System.Collections.Specialized.NameValueCollection
    new : System.Collections.IHashCodeProvider * System.Collections.IComparer -> System.Collections.Specialized.NameValueCollection
    new : int -> System.Collections.Specialized.NameValueCollection
    new : System.Collections.IEqualityComparer -> System.Collections.Specialized.NameValueCollection
    new : int * System.Collections.IEqualityComparer -> System.Collections.Specialized.NameValueCollection
    new : int * System.Collections.Specialized.NameValueCollection -> System.Collections.Specialized.NameValueCollection
    new : int * System.Collections.IHashCodeProvider * System.Collections.IComparer -> System.Collections.Specialized.NameValueCollection
    member Add : System.Collections.Specialized.NameValueCollection -> unit
    member Add : string * string -> unit
    member AllKeys : string []
    member Clear : unit -> unit
    member CopyTo : System.Array * int -> unit
    member Get : string -> string
    member Get : int -> string
    member GetKey : int -> string
    member GetValues : string -> string []
    member GetValues : int -> string []
    member HasKeys : unit -> bool
    member Item : string -> string with get, set
    member Item : int -> string
    member Remove : string -> unit
    member Set : string * string -> unit
  end

Full name: System.Collections.Specialized.NameValueCollection

  type: NameValueCollection
  implements: System.Collections.ICollection
  implements: System.Collections.IEnumerable
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.Serialization.IDeserializationCallback
  inherits: NameObjectCollectionBase
property NameValueCollection.AllKeys: string []
module Seq

from Microsoft.FSharp.Collections
val collect : ('T -> #seq<'U>) -> seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.collect
Multiple overloads
NameValueCollection.GetValues(index: int) : string []
NameValueCollection.GetValues(name: string) : string []
val map : ('T -> 'U) -> seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val v : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
val toList : seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
namespace System.Web
namespace System.Web.Mvc
namespace System.Collections.Generic
type HandleErrorAttribute =
  class
    inherit System.Web.Mvc.FilterAttribute
    new : unit -> System.Web.Mvc.HandleErrorAttribute
    member ExceptionType : System.Type with get, set
    member Master : string with get, set
    member OnException : System.Web.Mvc.ExceptionContext -> unit
    member View : string with get, set
  end

Full name: System.Web.Mvc.HandleErrorAttribute

  type: HandleErrorAttribute
  implements: Runtime.InteropServices._Attribute
  implements: IExceptionFilter
  inherits: FilterAttribute
  inherits: Attribute
type HomeController =
  class
    inherit Controller
    new : unit -> HomeController
    member Index : unit -> ActionResult
    member Register : unit -> ActionResult
  end

Full name: Formlets.HomeController

  type: HomeController
  implements: IController
  implements: IActionFilter
  implements: IAuthorizationFilter
  implements: IDisposable
  implements: IExceptionFilter
  implements: IResultFilter
  inherits: Controller
  inherits: ControllerBase
  inherits: MarshalByRefObject
type Controller =
  class
    inherit System.Web.Mvc.ControllerBase
    member ActionInvoker : System.Web.Mvc.IActionInvoker with get, set
    member Dispose : unit -> unit
    member HttpContext : System.Web.HttpContextBase
    member ModelState : System.Web.Mvc.ModelStateDictionary
    member Request : System.Web.HttpRequestBase
    member Response : System.Web.HttpResponseBase
    member RouteData : System.Web.Routing.RouteData
    member Server : System.Web.HttpServerUtilityBase
    member Session : System.Web.HttpSessionStateBase
    member TempDataProvider : System.Web.Mvc.ITempDataProvider with get, set
    member Url : System.Web.Mvc.UrlHelper with get, set
    member User : System.Security.Principal.IPrincipal
  end

Full name: System.Web.Mvc.Controller

  type: Controller
  implements: IController
  implements: IActionFilter
  implements: IAuthorizationFilter
  implements: IDisposable
  implements: IExceptionFilter
  implements: IResultFilter
  inherits: ControllerBase
  inherits: MarshalByRefObject
val inputInt : int Formlet
Multiple items
val int : 'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------

type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>

  type: int<'Measure>
  implements: IComparable
  implements: IConvertible
  implements: IFormattable
  implements: IComparable<int<'Measure>>
  implements: IEquatable<int<'Measure>>
  inherits: ValueType


--------------------

type int = int32

Full name: Microsoft.FSharp.Core.int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val inputDate : DateTime Formlet
val month : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val day : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val year : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
type DateTime =
  struct
    new : int64 -> System.DateTime
    new : int64 * System.DateTimeKind -> System.DateTime
    new : int * int * int -> System.DateTime
    new : int * int * int * System.Globalization.Calendar -> System.DateTime
    new : int * int * int * int * int * int -> System.DateTime
    new : int * int * int * int * int * int * System.DateTimeKind -> System.DateTime
    new : int * int * int * int * int * int * System.Globalization.Calendar -> System.DateTime
    new : int * int * int * int * int * int * int -> System.DateTime
    new : int * int * int * int * int * int * int * System.DateTimeKind -> System.DateTime
    new : int * int * int * int * int * int * int * System.Globalization.Calendar -> System.DateTime
    new : int * int * int * int * int * int * int * System.Globalization.Calendar * System.DateTimeKind -> System.DateTime
    member Add : System.TimeSpan -> System.DateTime
    member AddDays : float -> System.DateTime
    member AddHours : float -> System.DateTime
    member AddMilliseconds : float -> System.DateTime
    member AddMinutes : float -> System.DateTime
    member AddMonths : int -> System.DateTime
    member AddSeconds : float -> System.DateTime
    member AddTicks : int64 -> System.DateTime
    member AddYears : int -> System.DateTime
    member CompareTo : obj -> int
    member CompareTo : System.DateTime -> int
    member Date : System.DateTime
    member Day : int
    member DayOfWeek : System.DayOfWeek
    member DayOfYear : int
    member Equals : obj -> bool
    member Equals : System.DateTime -> bool
    member GetDateTimeFormats : unit -> string []
    member GetDateTimeFormats : System.IFormatProvider -> string []
    member GetDateTimeFormats : char -> string []
    member GetDateTimeFormats : char * System.IFormatProvider -> string []
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> System.TypeCode
    member Hour : int
    member IsDaylightSavingTime : unit -> bool
    member Kind : System.DateTimeKind
    member Millisecond : int
    member Minute : int
    member Month : int
    member Second : int
    member Subtract : System.DateTime -> System.TimeSpan
    member Subtract : System.TimeSpan -> System.DateTime
    member Ticks : int64
    member TimeOfDay : System.TimeSpan
    member ToBinary : unit -> int64
    member ToFileTime : unit -> int64
    member ToFileTimeUtc : unit -> int64
    member ToLocalTime : unit -> System.DateTime
    member ToLongDateString : unit -> string
    member ToLongTimeString : unit -> string
    member ToOADate : unit -> float
    member ToShortDateString : unit -> string
    member ToShortTimeString : unit -> string
    member ToString : unit -> string
    member ToString : string -> string
    member ToString : System.IFormatProvider -> string
    member ToString : string * System.IFormatProvider -> string
    member ToUniversalTime : unit -> System.DateTime
    member Year : int
    static val MinValue : System.DateTime
    static val MaxValue : System.DateTime
    static member Compare : System.DateTime * System.DateTime -> int
    static member DaysInMonth : int * int -> int
    static member Equals : System.DateTime * System.DateTime -> bool
    static member FromBinary : int64 -> System.DateTime
    static member FromFileTime : int64 -> System.DateTime
    static member FromFileTimeUtc : int64 -> System.DateTime
    static member FromOADate : float -> System.DateTime
    static member IsLeapYear : int -> bool
    static member Now : System.DateTime
    static member Parse : string -> System.DateTime
    static member Parse : string * System.IFormatProvider -> System.DateTime
    static member Parse : string * System.IFormatProvider * System.Globalization.DateTimeStyles -> System.DateTime
    static member ParseExact : string * string * System.IFormatProvider -> System.DateTime
    static member ParseExact : string * string * System.IFormatProvider * System.Globalization.DateTimeStyles -> System.DateTime
    static member ParseExact : string * string [] * System.IFormatProvider * System.Globalization.DateTimeStyles -> System.DateTime
    static member SpecifyKind : System.DateTime * System.DateTimeKind -> System.DateTime
    static member Today : System.DateTime
    static member TryParse : string * System.DateTime -> bool
    static member TryParse : string * System.IFormatProvider * System.Globalization.DateTimeStyles * System.DateTime -> bool
    static member TryParseExact : string * string * System.IFormatProvider * System.Globalization.DateTimeStyles * System.DateTime -> bool
    static member TryParseExact : string * string [] * System.IFormatProvider * System.Globalization.DateTimeStyles * System.DateTime -> bool
    static member UtcNow : System.DateTime
  end

Full name: System.DateTime

  type: DateTime
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: Runtime.Serialization.ISerializable
  implements: IComparable<DateTime>
  implements: IEquatable<DateTime>
  inherits: ValueType
val formlet : (string * DateTime * DateTime * int) Formlet
val name : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val order : DateTime

  type: DateTime
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: Runtime.Serialization.ISerializable
  implements: IComparable<DateTime>
  implements: IEquatable<DateTime>
  inherits: ValueType
val ship : DateTime

  type: DateTime
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: Runtime.Serialization.ISerializable
  implements: IComparable<DateTime>
  implements: IEquatable<DateTime>
  inherits: ValueType
val amount : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val x : HomeController

  type: HomeController
  implements: IController
  implements: IActionFilter
  implements: IAuthorizationFilter
  implements: IDisposable
  implements: IExceptionFilter
  implements: IResultFilter
  inherits: Controller
  inherits: ControllerBase
  inherits: MarshalByRefObject
member HomeController.Index : unit -> ActionResult

Full name: Formlets.HomeController.Index
property ControllerBase.ViewData: ViewDataDictionary
Multiple overloads
Controller.View() : ViewResult
Controller.View(view: IView) : ViewResult
Controller.View(viewName: string) : ViewResult
Controller.View(model: obj) : ViewResult
Controller.View(view: IView, model: obj) : ViewResult
Controller.View(viewName: string, model: obj) : ViewResult
Controller.View(viewName: string, masterName: string) : ViewResult
Controller.View(viewName: string, masterName: string, model: obj) : ViewResult
val box : 'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
val render : 'a Formlet -> Xml.Linq.XDocument

Full name: Formlets.Formlet.render
type ActionResult =
  class
    member ExecuteResult : System.Web.Mvc.ControllerContext -> unit
  end

Full name: System.Web.Mvc.ActionResult
member HomeController.Register : unit -> ActionResult

Full name: Formlets.HomeController.Register
val env : (string * string) list

  type: (string * string) list
  implements: Collections.IStructuralEquatable
  implements: IComparable<List<string * string>>
  implements: IComparable
  implements: Collections.IStructuralComparable
  implements: IEnumerable<string * string>
  implements: Collections.IEnumerable
val fromNV : Collections.Specialized.NameValueCollection -> (string * string) list

Full name: Formlets.Formlet.fromNV
property Controller.Request: HttpRequestBase
property HttpRequestBase.Form: Collections.Specialized.NameValueCollection
val orderDate : DateTime

  type: DateTime
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: Runtime.Serialization.ISerializable
  implements: IComparable<DateTime>
  implements: IEquatable<DateTime>
  inherits: ValueType
val shippingDate : DateTime

  type: DateTime
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: Runtime.Serialization.ISerializable
  implements: IComparable<DateTime>
  implements: IEquatable<DateTime>
  inherits: ValueType

No comments: