Logo




Subscribe:
RSS 2.0 | Atom 1.0
Categories:

Sign In


[Giagnocavo]Michael::Write()

# Friday, May 15, 2009
F# CTP and ASP.NET MVC ASPX: Scoping disposables

If you use ASP.NET MVC with F# CTP (Monday brings Beta 1 and probably many changes), you might run into an issue of how to scope certain things. For example, an IDisposable is used to create blocks, for things like an HTML form. Example in C#:

    <% using (Html.BeginForm()) {%>

 

        <fieldset><legend>Fields</legend>...</fieldset>

 

    <% } %>

 

This is needed because VB and C# didn't have any easy function/block syntax (VB 10 should fix this), and many C# developers are still wary of higher order functions. How does this play out in F#? First off, whitespace is important. This can get really messy with the current F# ASP.NET integration. Basically, always open the script blocks on a separate line, and indent from the first column. Example:

<% let i = 0

   this.Response.Write(sprintf "i = %d" i) %>

(Note that the 'this' variable is bound to the current page.) This fails:

Compiler Error Message: FS0010: Unexpected keyword 'let' or 'use' in expression. Expected incomplete structured construct at or before this point or other token

Source Error:

Line 111:                let mutable parameterContainer = parameterContainer
Line 112:                __w.Write("\r\n") |> ignore
Line 113:                   let i = 0
Line 114:                   this.Response.Write(sprintf "i = %d" i) 
Line 115:                __w.Write("\r\n    <h2>Create</h2>\r\n\r\n    ") |> ignore

Note how the <% is counted as space, so the let starts off indented 3 spaces. Instead, we need to write it so:

<%

let i = 0

this.Response.Write(sprintf "i = %d" i) %>

This works fine. You can also put the %> on the next line if you like. Now, on to ASP.NET MVC's IDisposable usage. A straightforward use of the F# using function won't work:

<b>Demo</b>

 

<%

using (Mvc.Html.FormExtensions.BeginForm this.Html) (fun _ -> %>

 

    <p>Inside a form</p>

 

<% ) %>

Compiler Error Message: FS0191: The mutable variable '__w' is used in an invalid way. Mutable variables may not be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.

Source Error:

Line 114:                __w.Write("\r\n\r\n<b>Demo</b>\r\n\r\n") |> ignore
Line 115:                  
Line 116:                using (Mvc.Html.FormExtensions.BeginForm this.Html) (fun _ -> 
Line 117:                __w.Write("\r\n\r\n    <p>Inside a form</p>\r\n\r\n") |> ignore
Line 118:                   ) 

As the error says, this is because the __w variable is mutable, so we can't play with it inside a lambda. I'm not sure if this will be worked around -- they'd have to change the codegen quite a bit, I'd think. As a side note, the F# CTP does not support C# extension methods (hence the verbose calling of BeginForm), but F# will eventually -- maybe in the Beta.

The way we must scope is with a use binding. There's no way I see to accomplish this with whitespace alone. Due to the ASPX translation process, this would probably be very error prone. Instead, we can simply put the use binding inside a do expression:

<b>Demo</b>

 

<%

do (use form = Mvc.Html.FormExtensions.BeginForm this.Html %>

 

<b>Inside the form</b>

 

<% ) %>

 

<b>Outside of the form</b>

The parentheses setup the scope exactly how we want it.

ASP.NET | FSharp
Friday, May 15, 2009 8:46:39 PM UTC  #    Comments [0]  |  Trackback

OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview