Showing posts with label formlets. Show all posts
Showing posts with label formlets. Show all posts

Tuesday, April 12, 2011

Formlets in C# and VB.NET (part 2)

In my last post I introduced CsFormlets, a wrapper around FsFormlets that brings formlets to C# and VB.NET. I showed how to model a typical registration form with CsFormlets. Now let's see how we can use formlets in ASP.NET MVC.

To recap, here's the form we're modeling:

form_cs

and here's the top-level formlet's signature, the one we'll reference in our ASP.NET MVC controller:

static Formlet<RegistrationInfo> IndexFormlet() { ... }

RegistrationInfo contains all information about this form. You can place this anywhere you want. I chose to put all formlets related to this form in a static class SignupFormlet, in a Formlets directory and namespace. In MVC terms, formlets fulfill the responsibilities of view (for the form), validation and binding.

We'll start with a regular controller with an action to show the formlet:

public class SignupController : Controller {
    [HttpGet]
    public ActionResult Index() {
        return View(model: SignupFormlet.IndexFormlet().ToString());
    }
}

Our view is trivial (modulo styles and layout), as it's only a placeholder for the formlet:

Views/Signup/Index.cshtml

@using (Html.BeginForm()) {
    @Html.Raw(Model)
    <input type="submit" value="Register" />
}

Now we need an action to handle the form submission. The simplest thing to do is handling the formlet manually (let this be case 1):

[HttpPost]
public ActionResult Index(FormCollection form) {
    var result = SignupFormlet.IndexFormlet().Run(form);
    if (!result.Value.HasValue())
        return View(model: result.ErrorForm.Render());
    var value = result.Value.Value;
    return RedirectToAction("ThankYou", new { name = value.User.FirstName + " " + value.User.LastName });
}

The problem with this approach is, if you want to test the action you have to build a test form, so you're actually testing both binding and the processing of the bound object. No problem, just extract methods to the desired level, e.g. (case 2)

[HttpPost]
public ActionResult Index(FormCollection form) {
    var result = SignupFormlet.IndexFormlet().Run(form);
    return Signup(result);
}

[NonAction]
public ActionResult Signup(FormletResult<RegistrationInfo> registration) {
    if (!registration.Value.HasValue())
        return View(model: registration.ErrorForm.Render());
    return Signup(registration.Value.Value);
}

[NonAction]
public ActionResult Signup(RegistrationInfo registration) {
    return RedirectToAction("ThankYou", new { name = registration.User.FirstName + " " + registration.User.LastName });
}

So we can easily test either Signup method.

Alternatively we can use some ASP.NET MVC mechanisms. For example, a model binder (case 3):

[HttpPost]
public ActionResult Index([FormletBind(typeof(SignupFormlet))] FormletResult<RegistrationInfo> registration) {
    if (!registration.Value.HasValue())
        return View(model: registration.ErrorForm.Render());
    var value = registration.Value.Value;
    return RedirectToAction("ThankYou", new { name = value.User.FirstName + " " + value.User.LastName });
}

By convention the method used to get the formlet is [action]Formlet (you can of course override this).

We can take this even further with an action filter (case 4):

[HttpPost]
[FormletFilter(typeof(SignupFormlet))]
public ActionResult Index(RegistrationInfo registration) {
    return RedirectToAction("ThankYou", new {name = registration.User.FirstName + " " + registration.User.LastName});
}

In this case the filter encapsulates checking the formlet for errors and automatically renders the default action view ("Index" in this case, but this is an overridable parameter) with the error form provided by the formlet. The formlet and filter ensure that the registration argument is never null or invalid when it hits the action.

However convenient they may be, the filter and model binder come at the cost of static type safety. Also, in a real-world application, case 4 is likely not flexible enough, so I'd probably go for the integration case 2 or 3.

Testing formlets

Testing formlets themselves is simple: you just give the formlet a form (usually a NameValueCollection, unless a file is involved), then assert over its result. Since formlets are composable you can easily test some part of it independently of other parts. For example, here's a test for the credit card expiration formlet checking that it correctly validates past dates:

[Fact]
public void CardExpiration_validates_past_dates() {
    var twoMonthsAgo = DateTime.Now.AddMonths(-2);
    var result = SignupFormlet.CardExpiration().Run(new NameValueCollection { 
        {"f0", twoMonthsAgo.Month.ToString()},
        {"f1", twoMonthsAgo.Year.ToString()},
    });
    Assert.False(result.Value.HasValue());
    Assert.True(result.Errors.Any(e => e.Contains("Card expired")));
}

Conclusion

CsFormlets provides composable, testable, statically type-safe validation and binding to C# / VB.NET web apps. Here I showed a possible ASP.NET MVC integration which took less than 200 LoC; integrating with other web frameworks should be similarly easy.

One thing I think I haven't mentioned is that CsFormlets' FormElements generates HTML5 form elements, just like FsFormlets. In fact, pretty much everything I have written about formlets applies to CsFormlets as well, since it's just a thin wrapper.

All code shown here is part of the CsFormlets sample app. Code repository is on github. CsFormlets depends on FsFormlets (therefore also the F# runtime) and runs on .NET 3.5 SP1.

Friday, April 8, 2011

Formlets in C# and VB.NET

All this talk about formlets in F#, by now you might think this is something that only works in functional languages. Nothing further from the truth. Tomáš Petříček has already blogged about formlets in C#, and I've been working on a wrapper around FsFormlets to be used in C# and VB.NET I called CsFormlets (I'm really good with names if you haven't noticed).

In this post I'll assume you already know about formlets. If you don't, I recommend reading Tomas' article. If you want to know more about my particular implementation of formlets, see my previous posts on the subject. If you're just too lazy to read all those lengthy articles, that's ok, read on, you'll still get a good feeling of formlets.

So if F# is a first-class .NET language, why is it necessary to wrap FsFormlets for C# consumption? Well, for one the formlet type is utterly unmanageable in C#. The formlet type in FsFormlets is (expressed in F#):

type 'a Formlet = 'a Error ErrorList XmlWriter Environ XmlWriter NameGen

where Error, ErrorList, etc, each are individual applicative functors. Type aliases in F# make it easy to hide the 'real' type underneath that, but unfortunately, C# doesn't support type aliases with type parameters, so the formlet type becomes this monster:

FSharpFunc<int, Tuple<Tuple<FSharpList<XNode>, FSharpFunc<FSharpList<Tuple<string, InputValue>>, Tuple<FSharpList<XNode>, Tuple<FSharpList<string>, FSharpOption<T>>>>>, int>>

And no, you can't always var your way out, so to keep this usable I wrapped this in a simpler Formlet<T> type.

Functions that use F# types like FSharpFunc<...> (obviously) and FSharpList<Tuple<T,U>> are wrapped so they use System.Func<...> and IEnumerable<KeyValuePair<T,U>> respectively. F# options are converted to/from nullables whenever possible. Extension methods are provided to work more easily with F# lists and option types. Active patterns (used in F# to match success or failure of formlet) are just not available. Also, applicative operators like <*>, <*, etc are just not accessible in C#, so I wrapped them in methods of Formlet<T>. This yields a fluent interface, as we'll see in a moment.

As usual, I'll demo the code with a concrete form, which looks like this:

form_cs

As I wanted to make this example more real-world than previous ones, I modeled it after the signup form of a real website, don't remember which one but it doesn't really matter. This time it even has decent formatting and styling!

As usual we'll build the form bottom-up.

Password

First the code to collect the password:

static readonly FormElements e = new FormElements();

static readonly Formlet<string> password = 
    Formlet.Tuple2<string, string>() 
        .Ap(e.Password(required: true).WithLabelRaw("Password <em>(6 characters or longer)</em>")) 
        .Ap(e.Password(required: true).WithLabelRaw("Enter password again <em>(for confirmation)</em>")) 
        .SatisfiesBr(t => t.Item1 == t.Item2, "Passwords don't match") 
        .Select(t => t.Item1) 
        .SatisfiesBr(t => t.Length >= 6, "Password must be 6 characters or longer");

Formlet.Tuple2 is just like "yields t2" in FsFormlets, i.e. it sets up a formlet to collect two values in a tuple. Unfortunately, type inference is not so good in C# so we have to define the types here. We'll see later some alternatives to this.

Ap() is just like <*> in FsFormlets.

SatisfiesBr() applies validation. Why "Br"? Because it outputs a <br/> before writing the error message. If no <br/> was present, the error "Password must be 6 characters or longer" would overflow and show in two lines, which looks bad.
This is defined as a simple extension method, implemented using the built-in Satisfies():

static IEnumerable<XNode> BrError(string err, List<XNode> xml) { 
    return xml.Append(X.E("br"), X.E("span", X.A("class", "error"), err)); 
}

static Formlet<T> SatisfiesBr<T>(this Formlet<T> f, Func<T, bool> pred, string error) { 
    return f.Satisfies(pred, 
        (_, x) => BrError(error, x), 
        _ => new[] { error }); 
}

Now you may be wondering about X.E() and X.A(). They're just simple functions to build System.Xml.Linq.XElements and XAttributes respectively.

Back to the password formlet: ever since C# 3.0, Select() is the standard name in C# for what is generally known as map, so I honor that convention in CsFormlets. In this case, it's used to discard the second collected value, since password equality has already been tested in the line above.

Account URL

Moving on, the formlet that collects the account URL:

static readonly Formlet<string> account = 
    Formlet.Single<string>() 
        .Ap("http://") 
        .Ap(e.Text(attributes: new AttrDict {{"required","required"}})) 
        .Ap(".example.com") 
        .Ap(X.E("div", X.Raw("Example: http://<b>company</b>.example.com"))) 
        .Satisfies(a => !string.IsNullOrWhiteSpace(a), "Required field") 
        .Satisfies(a => a.Length >= 2, "Two characters minimum") 
        .Satisfies(a => string.Format("http://{0}.example.com", a).IsUrl(), "Invalid account") 
        .WrapWith(X.E("fieldset"));

You should notice at least two weird things here. If you don't, you're not paying attention! :-)

First weird thing: I said Ap() is <*> , but you couldn't apply <*> to pure text (.Ap("http://")) or XML as shown here, only to a formlet! This is one of the advantages of C#: Ap() is overloaded to accept text and XML, in which case it lifts them to Formlet<Unit> and then applies <*
Because of these overloads Ap() could almost be thought of as append instead of apply.

Second weird thing: instead of writing e.Text(required: true) as in the password formlet, I explicitly used required just as HTML attribute. However, requiredness is checked server-side after all markup. This is for the same reason I defined SatisfiesBr() above: we wouldn't like the error message to show up directly after the input like this:

http:// Required field.example.com

Alternatively, I could have used a polyfill for browsers that don't support the required attribute, but I'm going for a zero-javascript solution here, and also wanted to show this flexibility.

It's also possible to define default conventions for all error messages in formlets (i.e. always show errors above the input, or below, or as balloons) but I won't show it here.

Oh, in case it's not evident, X.Raw() parses XML into System.Xml.Linq.XNodes.

User

Let's put things together in a User class

static readonly Formlet<User> user = 
    Formlet.Tuple5<string, string, string, string, string>() 
        .Ap(e.Text(required: true).WithLabel("First name")) 
        .Ap(e.Text(required: true).WithLabel("Last name")) 
        .Ap(e.Email(required: true).WithLabelRaw("Email address <em>(you'll use this to sign in)</em>")) 
        .Ap(password) 
        .WrapWith(X.E("fieldset")) 
        .Ap(X.E("h3", "Profile URL")) 
        .Ap(account) 
        .Select(t => new User(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5));

Nothing new here, just composing the password and account URL formlets along with a couple other inputs, yielding a User.

Card expiration

Let's tackle the last part of the form, starting with the credit card expiration:

static Formlet<DateTime> CardExpiration() { 
    var now = DateTime.Now; 
    var year = now.Year; 
    return Formlet.Tuple2<int, int>() 
        .Ap(e.Select(now.Month, Enumerable.Range(1, 12))) 
        .Ap(e.Select(year, Enumerable.Range(year, 10))) 
        .Select(t => new DateTime(t.Item2, t.Item1, 1).AddMonths(1)) 
        .Satisfies(t => t > now, t => string.Format("Card expired {0:#} days ago!", (now-t).TotalDays)) 
        .WrapWithLabel("Expiration date<br/>"); 
}

This formlet, unlike previous ones, is a function, because it depends on the current date. It has two <select/> elements: one for the month, one for the year, by default set to the current date.

Billing info

Now we use the card expiration formlet in the formlet that collects other billing data:

static readonly IValidationFunctions brValidationFunctions = 
    new Validate(new ValidatorBuilder(BrError));

static Formlet<BillingInfo> Billing() { 
    return Formlet.Tuple4<string, DateTime, string, string>() 
        .Ap(e.Text(required: true).Transform(brValidationFunctions.CreditCard).WithLabel("Credit card number")) 
        .Ap(CardExpiration()) 
        .Ap(e.Text(required: true).WithLabel("Security code")) 
        .Ap(e.Text(required: true).WithLabelRaw("Billing ZIP <em>(postal code if outside the USA)</em>")) 
        .Select(t => new BillingInfo(t.Item1, t.Item2, t.Item3, t.Item4)) 
        .WrapWith(X.E("fieldset")); 
}

Transform() is just a simple function application. brValidationFunctions.CreditCard is a function that applies credit card number validation (the Luhn algorithm). The validation function is initialized with the same BrError() convention I defined above, i.e. it writes a <br/> and then the error message.

Top formlet

Here's the top-level formlet, the one we'll use in the controller to show the entire form and collect all values:

static Formlet<RegistrationInfo> IndexFormlet() { 
    return Formlet.Tuple2<User, BillingInfo>() 
        .Ap(X.E("h3", "Enter your details")) 
        .Ap(user) 
        .Ap(X.E("h3", "Billing information")) 
        .Ap(Billing()) 
        .Select(t => new RegistrationInfo(t.Item1, t.Item2)); 
}

LINQ & stuff

I've been using Formlet.Tuple in these examples, but you could also use Formlet.Yield, which behaves just like "yields" in FsFormlets. In F# this is no problem because functions are curried, but this is not the case in C#. Even worse, type inference is really lacking in C# compared to F#. This makes Formlet.Yield quite unpleasant to use:

Formlet.Yield<Func<User,Func<BillingInfo,RegistrationInfo>>>((User a) => (BillingInfo b) => new RegistrationInfo(a,b))

With a little function to help with inference such as this one, it becomes

Formlet.Yield(L.F((User a) => L.F((BillingInfo b) => new RegistrationInfo(a, b))))

Still not very pretty, so I prefer to use Formlet.Tuple and then project the tuple to the desired type.

Another way to define formlets in CsFormlets is using LINQ syntax. Tomas explained in detail how this works in a recent blog post. For example, the last formlet defined with LINQ:

static Formlet<RegistrationInfo> IndexFormletLINQ() { 
    return from x in Formlet.Raw(X.E("h3", "Enter your details")) 
           join u in user on 1 equals 1 
           join y in Formlet.Raw(X.E("h3", "Billing information")) on 1 equals 1 
           join billing in Billing() on 1 equals 1 
           select new RegistrationInfo(u, billing); 
}

Also, where can be used to apply validation, although you can't define the error message in each case or where it will be displayed.

The LINQ syntax has some pros and cons.

Pros

  • Less type annotations required.
  • No need to define at the start of the formlet what values and types we will collect.

Cons

  • join and on 1 equals 1 look somewhat odd.
  • Pure text and XML need to be explicitly lifted.
  • Less flexible than chaining methods. If you use where to apply validation, you can't define the message. If you want to use Satisfies(), WrapWith() or any other extension method, you have break up the formlet expression.

Personally, I prefer chaining methods over LINQ, but having a choice might come in handy sometimes.

VB.NET

The title of this post is "Formlets in C# and VB.NET", so what is really different in VB.NET? We could, of course, translate everything in this post directly to VB.NET. But VB.NET has a distinctive feature that is very useful for formlets: XML literals. Instead of:

(C#) xml.Append(X.E("br"), X.E("span", X.A("class", "error"), err));

In VB.NET we can write:

(VB.NET) xml.Append(<br/>, <span class="error"><%= err %></span>)

which is not only clearer, but also more type-safe: you can't write something like <span 112="error">, it's a compile-time error.

To be continued...

This post is too long already so I'll leave ASP.NET MVC integration and testing for another post. If you want to play with the bits, the CsFormlets code is here. All code shown here is part of the CsFormlets sample app. Keep in mind that you also need FsFormlets, which is included as a git submodule, so after cloning CsFormlets you have to init the submodules.

Tuesday, March 29, 2011

Integrating Formlets with Wing Beats and Figment

I've blogged before about formlets, a nice abstraction of HTML forms. I started with a basic implementation, then showed validation. Now I'll present a non-toy implementation of formlets I called FsFormlets (really original name, I know). By "non-toy" I mean this implementation is not a proof of concept or just for didactic purposes, but is meant to be eventually production-quality.

I don't like to explain things in a vacuum, so I'll use a typical registration form to show how it works:

form2

Even though FsFormlets can be used to generate HTML on its own, there's a much better tool for this in F#: Wing Beats. Wing Beats is an EDSL in F# to generate HTML, much like SharpDOM in C# or Blaze in Haskell, or Eliom's XHTML.M in OCaml (except that XHTML.M actually validates HTML statically). So I've added a module to integrate FsFormlets to Wing Beats. This integration is a separate assembly; FsFormlets is stand-alone (it only requires .NET 3.5 SP1 and the F# runtime). We'll use FsFormlets to express forms, and Wing Beats to express the rest of HTML. Also, we'll handle web requests with Figment, with the help of some more glue code to integrate it with FsFormlets and Wing Beats.

Layout

Let's start by defining a layout in Wing Beats:

let e = XhtmlElement()

let layout title body = 
    [ 
        e.DocTypeHTML5 
        e.Html [ 
            e.Head [ 
                e.Title [ &title ] 
                e.Style [ 
                    &".error {color:red;}" 
                    &"body {font-family:Verdana,Geneva,sans-serif; line-height: 160%;}" 
                ] 
            ] 
            e.Body [ 
                yield e.H1 [ &title ] 
                yield! body 
            ] 
        ] 
    ]

No formlets so far, this is all pure HTML, expressed as a regular function. Now we'll build the form bottom-up.

ReCaptcha

FsFormlets already includes a reCaptcha formlet (I'll show its innards in a future post). We just have to configure it with a pair of public and private key (get it here) before using it:

let reCaptcha = reCaptcha {PublicKey = "your_public_key"; PrivateKey = "your_private_key"; MockedResult = None}

MockedResult lets you skip the actual validation web call and force a result, for testing purposes.

Date input

Now the date formlet, which is built from three inputs, plus labels and validation:

let f = e.Formlets

let dateFormlet : DateTime Formlet = 
    let baseFormlet = 
        yields t3 
        <*> (f.Text(maxlength = 2, attributes = ["type","number"; "min","1"; "max","12"; "required","required"; "size","3"]) |> f.WithLabel "Month: ") 
        <*> (f.Text(maxlength = 2, attributes = ["type","number"; "min","1"; "max","31"; "required","required"; "size","3"]) |> f.WithLabel "Day: ") 
        <*> (f.Text(maxlength = 4, attributes = ["type","number"; "min","1900"; "required","required"; "size","5"]) |> f.WithLabel "Year: ") 
    let isDate (month,day,year) = 
        let pad n (v: string) = v.PadLeft(n,'0') 
        let ymd = sprintf "%s%s%s" (pad 4 year) (pad 2 month) (pad 2 day) 
        DateTime.TryParseExact(ymd, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None) |> fst 
    let dateValidator = err isDate (fun _ -> "Invalid date") 
    baseFormlet 
    |> satisfies dateValidator 
    |> map (fun (month,day,year) -> DateTime(int year,int month,int day))

Here, baseFormlet is of type (string * string * string) Formlet, that is, it collects values in their raw form. This baseFormlet is then validated to make sure it's a date, and finally mapped to a DateTime. Note the use of the 'number' input type (a HTML5 input) and also HTML5 validation attributes (required, min, max)

Password input

The password formlet is next:

let doublePassword = 
    let compressedLength (s: string) = 
        use buffer = new MemoryStream() 
        use comp = new DeflateStream(buffer, CompressionMode.Compress) 
        use w = new StreamWriter(comp) 
        w.Write(s) 
        w.Flush() 
        buffer.Length 
    let isStrong s = compressedLength s >= 106L 
    let f = 
        yields t2 
        <*> (f.Password(required = true) |> f.WithLabel "Password: ") 
        <+ e.Br() 
        <*> (f.Password(required = true) |> f.WithLabel "Repeat password: ") 
    let areEqual (a,b) = a = b 
    f 
    |> satisfies (err areEqual (fun _ -> "Passwords don't match")) 
    |> map fst 
    |> satisfies (err isStrong (fun _ -> "Password too weak"))

There are two things we validate here for passwords: first, the two entered passwords must match; and second, it must be strong enough. To measure password strength I use the compression technique I described in my last post.

The <+ operator is used to add pure markup to the formlet, using Wing Beats.

Also note the 'required = true' parameter. In the date formlet, we just used required=required as an HTML attribute. Here we used the optional argument required=true, which validates the formlet both at the client (with a required=required HTML attribute) and the server (it has a satisfies clause). In the date formlet we don't really care to validate server-side if the user filled out each input, we just want to know if it's a valid date or not.

The optional parameter 'maxlength' does a similar thing: it outputs a maxlength attribute, and also checks the maximum length of he POSTed value. Sure all browsers implement maxlength properly, but anyone can easily craft a request (e.g. with cUrl, no need to program at all) to circumvent it. I see malicious bots doing things like this every day. This makes sure the data is really valid before you start processing it.

Putting it all together

Moving on, let's write the final formlet and store the posted information in a record:

type PersonalInfo = { 
    Name: string 
    Email: string 
    Password: string 
    DateOfBirth: DateTime 
} 

let registrationFormlet ip = 
    yields (fun n e p d -> 
                { Name = n; Email = e; Password = p; DateOfBirth = d }) 
    <*> (f.Text(required = true) |> f.WithLabel "Name: ") 
    <+ e.Br() 
    <*> (f.Email(required = true) |> f.WithLabel "Email: ") 
    <+ e.Br() 
    <*> doublePassword 
    <+ e.Br() 
    <+ &"Date of birth: " <*> dateFormlet 
    <+ e.Br() 
    <+ &"Please read very carefully these terms and conditions before registering for this online program, blah blah blah" 
    <+ e.Br() 
    <* (f.Checkbox(false) |> satisfies (err id (fun _ -> "Please accept the terms and conditions")) |> f.WithLabel "I agree to the terms and conditions above") 
    <* reCaptcha ip

Some notes about this last snippet:

  • f.Email(required = true) generates an <input type="email" required="required"/> (again, HTML5 !), all server-validated.
  • Unlike previous formlets, this one is a function, because reCaptcha needs the client's IP address to validate.

If you've read my previous posts about formlets, you may be wondering why I'm using things like f.Text() and f.Checkbox() (which are members of an object) instead of the regular functions input and checkbox. Those functions are also present in FsFormlets and you may use them interchangeably with the object-style formlets, e.g. instead of f.CheckBox(false) you can write checkbox false []. The object-style formlets build on top of functional formlets, adding optional parameters for validation. They also integrate more seamlessly with Wing Beats.

And we're done with the form! Now let's build the actual page that contains it. Using the layout we wrote earlier:

let s = e.Shortcut

let registrationPage form = 
    layout "Registration" [ 
        s.FormPost "" [ 
            e.Fieldset [ 
                yield e.Legend [ &"Please fill the fields below" ] 
                yield!!+form 
                yield e.Br() 
                yield s.Submit "Register!" 
            ] 
        ] 
    ]

This is also pure Wing Beats markup, I think it doesn't need much explanation except for the "yield!!+form" which indicates where to render the formlet (yes, the operator doesn't look very friendly, I'll probably change it). Note how easy it is to compose pieces of potentially reusable HTML with Wing Beats as they're just functions.

Handling requests with Figment

Now all we need to do is bind the formlet to a URL to render the page:

get "register" (fun _ -> registrationFormlet "" |> renderToXml |> registrationPage |> Result.wbview)

and handle the form POST:

post "register" (fun ctx -> 
    let env = EnvDict.fromFormAndFiles ctx.Request 
    match run (registrationFormlet ctx.IP) env with 
    | Success v -> Result.redirectf "thankyou?n=%s" v.Name 
    | Failure(errorForm,_) -> errorForm |> registrationPage |> Result.wbview)

Oh, I almost forgot the little "thank you" after-registration page:

get "thankyou" (fun ctx -> Result.contentf "Thank you for registering, %s" ctx.QueryString.["n"])

Now, this workflow we've just modeled is pretty common:

  1. Show form.
  2. User submits form.
  3. Validate form. If errors, show form again to user.
  4. Process form data.
  5. Redirect.

So it's worthy of abstraction. The only moving parts are the formlet, the page and what to do on successful validation, so instead of mapping get and post individually we can say:

formAction "register" { 
    Formlet = fun ctx -> registrationFormlet ctx.IP 
    Page = fun _ -> registrationPage 
    Success = fun _ v -> Result.redirectf "thankyou?n=%s" v.Name 
}

HTML5 ready

Implementation of HTML5 in browsers has exploded in 2010 (see the beautiful html5readiness.com for reference). In particular, HTML5 forms are already implemented in Chrome 10, Opera and Firefox 4 (with caveats). Safari and IE haven't implemented it yet (at least not in versions 5.0.4 and 9 respectively), but WebKit already supports it so I guess Safari and other WebKit-based browsers will implement this in the short-term. So there's little reason not to use the new elements and attributes right now, as long as you also have server-side validation.

For example, here's how Chrome validates required fields when trying to submit:

registration1

Whereas in Safari 5.0.4/Windows the server-side validation kicks in:

form-safari

If you want browser-side validation in Safari, IE, etc, you can easily use an unobtrusive polyfill. A thorough list of cross-browser HTML5 polyfills is available here.

For example, applying jQuery.tools is as easy as:

let jsValidation = 
    e.Div [ 
        s.JavascriptFile "http://cdn.jquerytools.org/1.2.5/full/jquery.tools.min.js" 
        e.Script [ &"$('form').validator();" ] 
    ]

and putting it at the bottom of the Wing Beats layout.

All code posted here is part of the sample Figment app.

FsFormlets source code is here.

Tuesday, February 8, 2011

Validation in formlets

Last time, we broke up formlets into primitive applicative functors, then composed these primitives to produce formlets.

So far, if someone entered an unexpected value in our little implementation of formlets (e.g. "abc" in an int Formlet), we'd get a nasty exception and there wasn't much we could do about it because we couldn't trace the exception to the formlet that was producing it. What we want, instead of an exception, is something that accumulates errors without interrupting the computation flow. Applicative functors, being oblivious, are perfect for that. More concretely, we want to:

  1. Know if the formlet was able to collect the value or not, and 
  2. if it couldn't, get the formlet rendered with the values the user entered and error messages, so we can re-display this second form, giving the user a chance to correct his mistakes.

The first item is a job for the option type. We'll return Some v if the formlet was successful and None if it wasn't.

For the second, we already have a XmlWriter applicative, so we could just reuse it to build this "error form".

Recall the type of formlets as we defined it last time:

type 'a Formlet = 'a Environ XmlWriter NameGen

Adding the two concerns we just mentioned, the signature becomes:

type 'a Formlet = 'a Error XmlWriter Environ XmlWriter NameGen

where Error is the Maybe monad in applicative form (remember that every monad is also an applicative functor).

This is what I was talking about when I mentioned formlets being extensible: you can extend formlet features by composing additional applicatives.

By the way, here's the same formlet type expressed using C# bracket style (which F# also supports):

type Formlet<'a> = NameGen<XmlWriter<Environ<XmlWriter<Error<'a>>>>>

See now why I prefer ML syntax? Bracket style looks quite messy once you start nesting many types.

Anyway, it's not enough to compose these applicatives, we also need some way to define how to validate things and how to show error messages. Let's define a validator type:

/// Validator type. 
/// Fst determines if value is valid 
/// Snd builds an error message 
type 'a Validator = ('a -> bool) * ('a -> xml_item list -> xml_item list)

and a function that attaches a validator to a formlet:

satisfies : 'a Validator -> 'a Formlet -> 'a Formlet

(I'm not going to bore you with implementation details in this post)
So we can now write:

let isInt = Int32.TryParse >> fst 
let intErrorMsg a xml = xml @ [ Tag("span", ["class","error"], [Text(sprintf "'%s' is not a valid number" a)]) ] 
let inputInt = input |> satisfies (isInt, intErrorMsg) |> lift int

When rendered the first time, this formlet doesn't show anything different:

printfn "%s" (render inputInt)

<input name="input_0" value=""/>

If we enter an invalid value, validation kicks in:

let env = EnvDict.fromValueSeq ["input_0","abc"] 
match run inputInt env with 
| _, Some v -> printfn "Formlet successful, value %d" v 
| errorForm, None -> printfn "Formlet unsuccessful, error form: \n%A" (XmlWriter.render errorForm)

This will print:

Formlet unsuccessful, error form: 
<div> 
    <input name="input_0" value="abc" /> 
    <span class="error">'abc' is not a valid number</span> 
</div>

We might want to wrap all that XML/HTML manipulation in order to make defining validators easier, for example:

/// <summary> 
/// Constructs a validator 
/// </summary> 
/// <param name="isValid">Determines if value is valid</param> 
/// <param name="errorMsg">Builds the error message</param> 
let err (isValid: 'a -> bool) (errorMsg: 'a -> string) : 'a Validator = 
    let addError value xml = 
        [ Tag("span", ["class","errorinput"], xml) 
          Tag("span", ["class","error"], [Text(errorMsg value)]) ]
    isValid, addError

Now we can code inputInt as:

let inputInt = input |> satisfies (err isInt (sprintf "'%s' is not a valid number")) |> lift int

We can chain as many validators as we want for any formlet. For example, here's a formlet that checks that the submitted value falls within a specified range:

let isInRange min max n = n >= min && n <= max
let inputRange min max = inputInt |> satisfies (err (isInRange min max) (fun _ -> sprintf "Value must be between %d and %d" min max))
let input10to40 = inputRange 10 40

Note how I used inputInt as the starting point for inputRange. inputInt is already validated for integers, so any non-integers values fed to inputRange will fail with the same error message as before. inputRange adds further validation on top of inputInt's validation. This is another example of the composability of formlets.

I put up a fully-fledged implementation of formlets, including validation, on github. There are some minor differences with what I've written so far about formlets. I'll blog about these differences soon.

As with all my previous articles on formlets, I have to give credit to the Links team; these blog posts and code are mostly just a rehash of their papers, which I can't recommend enough.

Tuesday, January 25, 2011

A factored implementation of formlets in F#

In my last post I showed a bare bones implementation of formlets. You may have noticed that formlets do a lot of stuff:

  • Generating form element names
  • Handling submitted values
  • Building and rendering HTML

That's a lot of responsibilities for a single module. Even the type of a formlet reflects this, it's pretty complex:

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

And we haven't even looked at adding validation, which would complicate things further. Bottom line: there is no clear separation of concerns.

The good news are: we already have identified those concerns (the bulleted list above), and applicative functors are easily composable, so we can model each of those concerns as independent applicative functors, then compose them to yield formlets.

HTML building

As usual, we start by defining types: the same definition for XML trees:

type xml_item =
    | Text of string
    | Tag of string * (string*string) list * xml_item list

And the type of the applicative functor itself:

type 'a XmlWriter = xml_item list * 'a

A XML forest and something else. That 'something else' is what will enable the composition of this applicative with others.

Finally, the implementation. If you compare this with the implementation of formlets described in the previous post, function by function, you'll see that this is effectively a stripped down applicative that only deals with XML.

module XmlWriter =
    let puree (v: 'a) : 'a XmlWriter = [],v
    let (<*>) (f: ('a -> 'b) XmlWriter) (a: 'a XmlWriter) : 'b XmlWriter =
        fst f @ fst a, (snd f) (snd a)
    let lift f a = puree f <*> a
    let lift2 f a b = puree f <*> a <*> b
    let plug (f: xml_item list -> xml_item list) (a: 'a XmlWriter) : 'a XmlWriter =
        f (fst a), snd a
    let xml (e: xml_item list): unit XmlWriter = e,()
    let text (s: string) : unit XmlWriter =
        xml [Text s]
    let tag (t: string) (attr: (string*string) list) (v: 'a XmlWriter) : 'a XmlWriter =
        plug (fun x -> [Tag(t, attr, x)]) v

    open System.Xml.Linq
    let render (xml: xml_item list) : XDocument =
        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

The only additional function here is plug, which maps the XML forest in a XmlWriter applicative. It's not of much use here and could be inlined, but it will be useful later to implement validation (in a future post).

Environment handling

The environment (or collector) applicative is one that fetches (collects) a value from the environment (usually Request.Form in ASP.NET). As such, its type is

type 'a Environ = (string*string) list -> 'a

The implementation:

module Environ =
    let puree v = fun env -> v
    let (<*>) (f: ('a -> 'b) Environ) (a: 'a Environ) : 'b Environ =
        fun env ->
            let g = f env
            g(a(env))
    let lift f a = puree f <*> a
    let lookup (n: string) : string Environ =
        fun env ->
            match List.tryFind (fun (k,_) -> k = n) env with
            | Some (_,v) -> v
            | _ -> failwithf "Key %s not found in environment" n

Note the lookup function, it's an Environ constructor that implements the core functionality of "given this key and this environment, give me the corresponding value". It's pretty much a dictionary in applicative form.

We'll also add here a helper function to convert a NameValueCollection (like Request.Form) to an environment:

    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

Name generation

We've seen how formlets automatically generate form element names. While the actual generation is provided by nextName, the counter used is carried over from application to application, since this is purely functional code, without state:

type 'a NameGen = int -> 'a * int
module NameGen =
    let puree (v : 'a) : 'a NameGen = 
        fun gen -> v,gen
    let (<*>) (f: ('a -> 'b) NameGen) (a: 'a NameGen) : 'b NameGen = 
        fun gen ->
            let v,gen = f gen
            let w,gen = a gen
            v w, gen
    let lift f a = puree f <*> a
    let lift2 f a b = puree f <*> a <*> b
    let nextName : string NameGen =
        fun gen ->
            "input_" + gen.ToString(), gen+1
    let run (c: 'a NameGen) : 'a = fst (c 0)

Here's a simple example of name generation:

> open NameGen;; 
> let names = puree (printfn "%s %s") <*> nextName <*> nextName;; 
> run names;; 
input_0 input_1

Composing applicatives

Applicative functors are easily composable, and the composition of any two applicatives is an applicative (math people say they're closed under composition). Interestingly, monads are not closed under composition, which as far as I understand is the reason for having monad transformers, but this doesn't seem to be a problem in F# since monads aren't as ubiquitous as in Haskell anyway.

Composing two applicatives is as simple as applying one applicative's pure to the other applicative's pure, and lifting <*>. Again, since we can't abstract type constructors, we can't perform composition generically, but we can always do it ad-hoc.

type 'a EnvironXmlWriter = 'a Environ XmlWriter

Expanding the types, this means

type 'a EnvironXmlWriter = xml_item list * ((string * string) list -> 'a)

The implementation:

module EnvironXmlWriter =
    let puree (v: 'a) : 'a Environ XmlWriter = 
        v |> Environ.puree |> XmlWriter.puree 
    let (<*>) (f: ('a -> 'b) Environ XmlWriter) (a: 'a Environ XmlWriter) : 'b Environ XmlWriter = 
        XmlWriter.lift2 Environ.(<*>) f a
    let lift f a = puree f <*> a

We'll also add a refine function that lets us build the composition from the "wrapping" applicative (in our case XmlWriter). Building it from the nested applicative is as easy as applying XmlWriter.pure.

    let refine (x: 'a XmlWriter) : 'a Environ XmlWriter =
        XmlWriter.lift Environ.puree x

Example:

let input : string Environ XmlWriter =  
    let xml = [Tag("input",["name","firstname"],[])] 
    let lookup = Environ.lookup "firstname"
    xml,lookup

This is almost a formlet. Just like a fully-fledged formlet, it has a XML part that can be rendered, and an associated collector. The only thing missing here is the automatic name generation applied to both the XML element and the collector.

Finally Formlets

All we have to do now is compose EnvironXmlWriter with NameGen to yield Formlets. Its type is:

type 'a Formlet = 'a EnvironXmlWriter NameGen

or

type 'a Formlet = 'a Environ XmlWriter NameGen

or if we expand each type:

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

which is exactly what we had in the last post. As you can see, factoring it to primitive applicatives has given us a clearer definition.

The implementation of formlets is almost trivial now:

module Formlet =
    let puree v : _ Formlet = v |> EnvironXmlWriter.puree |> NameGen.puree
    let (<*>) (f: ('a -> 'b) Formlet) (a: 'a Formlet) : 'b Formlet =
        NameGen.lift2 EnvironXmlWriter.(<*>) f a
    let lift f a : _ Formlet = 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
    let xml (x: xml_item list) : unit Formlet =
        NameGen.puree (EnvironXmlWriter.refine (XmlWriter.xml x))
    let text (s: string) : unit Formlet =
        xml [Text s]
    let tag (t: string) (attr: (string*string) list) (f: 'a Formlet) : 'a Formlet =
        NameGen.lift (XmlWriter.tag t attr) f
    let input : string Formlet =
        let xml name = XmlWriter.tag "input" ["name",name]
        let lookup name = XmlWriter.puree (Environ.lookup name)
        let tag name = xml name (lookup name)
        NameGen.lift tag NameGen.nextName
    let br: unit Formlet = xml [Tag("br",[],[])]
    let run (f: 'a Formlet) : 'a Environ =
        NameGen.run f |> snd

    open System.Xml.Linq
    let render (f: _ Formlet) = 
        NameGen.run f |> fst |> XmlWriter.render
F# Web Snippets

Factoring formlets like this not only has the advantage of producing cleaner code, but also enables the reusability of each applicative functor and extending formlets more easily, as we'll see in a future post.

Full source code is here. This implementation is interchangeable with the one in my last post, the project includes both implementations so you can compare them side by side. Most of this code was taken almost verbatim from the original papers on formlets.

namespace Formlets
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
val puree : 'a -> int -> 'a * int

Full name: Formlets.NameGen.puree
val v : 'a
type 'a NameGen = int -> 'a * int

Full name: Formlets.NameGen<_>
val gen : 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) NameGen
val a : 'a NameGen
val v : ('a -> 'b)
val w : 'a
val lift : ('a -> 'b) -> 'a NameGen -> 'b NameGen

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

Full name: Formlets.NameGen.lift2
val f : ('a -> 'b -> 'c)
val b : 'b NameGen
val nextName : int -> string * int

Full name: Formlets.NameGen.nextName
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>
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 run : 'a NameGen -> 'a

Full name: Formlets.NameGen.run
val c : 'a NameGen
val fst : ('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
type 'a Environ = (string * string) list -> 'a

Full name: Formlets.Environ<_>
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
val puree : 'a -> 'b -> 'a

Full name: Formlets.Environ.puree
val env : 'b
val f : ('a -> 'b) Environ
val a : 'a Environ
val env : (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 g : ('a -> 'b)
val lift : ('a -> 'b) -> 'a Environ -> 'b Environ

Full name: Formlets.Environ.lift
val lookup : string -> (string * string) list -> string

Full name: Formlets.Environ.lookup
val n : 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>
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 tryFind : ('T -> bool) -> 'T list -> 'T option

Full name: Microsoft.FSharp.Collections.List.tryFind
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>
union case Option.Some: 'T -> Option<'T>
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 failwithf : Printf.StringFormat<'T,'Result> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.failwithf
namespace System
namespace System.Collections
namespace System.Collections.Specialized
val fromNV : NameValueCollection -> (string * string) list

Full name: Formlets.Environ.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 toList : seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
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
union case xml_item.Text: string -> xml_item
union case xml_item.Tag: string * (string * string) list * xml_item list -> xml_item
type 'a XmlWriter = xml_item list * 'a

Full name: Formlets.XmlWriter<_>
val puree : 'a -> 'a XmlWriter

Full name: Formlets.XmlWriter.puree
val f : ('a -> 'b) XmlWriter
val a : 'a XmlWriter
val snd : ('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val lift : ('a -> 'b) -> xml_item list * 'a -> 'b XmlWriter

Full name: Formlets.XmlWriter.lift
val lift2 : ('a -> 'b -> 'c) -> xml_item list * 'a -> xml_item list * 'b -> 'c XmlWriter

Full name: Formlets.XmlWriter.lift2
val b : 'b XmlWriter
val plug : (xml_item list -> xml_item list) -> xml_item list * 'a -> 'a XmlWriter

Full name: Formlets.XmlWriter.plug
val f : (xml_item list -> xml_item list)
val xml : xml_item list -> unit XmlWriter

Full name: Formlets.XmlWriter.xml
val e : 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 XmlWriter

Full name: Formlets.XmlWriter.text
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>
val tag : string -> (string * string) list -> xml_item list * 'a -> 'a XmlWriter

Full name: Formlets.XmlWriter.tag
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 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 v : 'a XmlWriter
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
namespace System.Xml
namespace System.Xml.Linq
val render : xml_item list -> XDocument

Full name: Formlets.XmlWriter.render
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
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)
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 * 'a -> XObject)
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 value : 'a
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 * 'a) list -> XObject list)
val attr : (string * 'a) list

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

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

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

  type: 'a list
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<List<'a>>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
  implements: System.Collections.Generic.IEnumerable<'a>
  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
val puree : 'a -> 'a Environ XmlWriter

Full name: Formlets.EnvironXmlWriter.puree
Multiple items
module Environ

from Formlets

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

type 'a Environ = (string * string) list -> 'a

Full name: Formlets.Environ<_>
Multiple items
module XmlWriter

from Formlets

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

type 'a XmlWriter = xml_item list * 'a

Full name: Formlets.XmlWriter<_>
val f : ('a -> 'b) Environ XmlWriter
val a : 'a Environ XmlWriter
val lift : ('a -> 'b) -> xml_item list * 'a Environ -> 'b Environ XmlWriter

Full name: Formlets.EnvironXmlWriter.lift
val refine : xml_item list * 'a -> 'a Environ XmlWriter

Full name: Formlets.EnvironXmlWriter.refine
val x : 'a XmlWriter
type 'a Formlet = 'a Environ XmlWriter NameGen

Full name: Formlets.Formlet<_>
Multiple items
module NameGen

from Formlets

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

type 'a NameGen = int -> 'a * int

Full name: Formlets.NameGen<_>
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
Multiple items
module Formlet

from Formlets

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

type 'a Formlet = 'a Environ XmlWriter NameGen

Full name: Formlets.Formlet<_>
val puree : 'a -> 'a Formlet

Full name: Formlets.Formlet.puree
module EnvironXmlWriter

from Formlets
val f : ('a -> 'b) Formlet
val a : 'a Formlet
val lift : ('a -> 'b) -> 'a Formlet -> 'b Formlet

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

Full name: Formlets.Formlet.lift2
val b : 'b Formlet
val f : 'a Formlet
val a : 'b Formlet
val z : 'b
val z : 'a
val xml : xml_item list -> unit Formlet

Full name: Formlets.Formlet.xml
val text : string -> unit Formlet

Full name: Formlets.Formlet.text
val tag : string -> (string * string) list -> 'a Formlet -> 'a Formlet

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

Full name: Formlets.Formlet.input
val xml : (string -> 'a XmlWriter -> 'a XmlWriter)
val lookup : (string -> string Environ XmlWriter)
val tag : (string -> string Environ XmlWriter)
val br : unit Formlet

Full name: Formlets.Formlet.br
val run : 'a Formlet -> 'a Environ

Full name: Formlets.Formlet.run
val render : 'a Formlet -> XDocument

Full name: Formlets.Formlet.render