Archive for the ‘F#’ Category
Embedded Domain specific languages (EDSL) are a great way of presenting functionality in a programming language for a solving a specific problem. Linq in VB.NET and C# is a great example. The F# language provides programming constructs for creating EDSLs – a good is example is this Logo implementation.
When writing web parts in C# for SharePoint the System.Web “table” class or “table” HTML elements are most often used for controlling layout (“div” aficionados keep your ears covered…). The code quickly gets ugly and difficult to manage – there’s no direct control to ensure correct matching of opening and closing of tags. So how about writing an EDSL that provides a syntax and constructs for doing this?
Following on from a previous post on creating Web Parts in F# let’s look first at how the EDSL might be used. First, the mundane stuff for declaring and creating the System.Web controls – this will create a label and two input boxes:
type public HelloWorldWP() = inherit WebPart() let labelRow1 = new Label() let inputRow1 = new TextBox() let inputRow2 = new TextBox() override x.CreateChildControls() = labelRow1.Text <- "Row 1 Label" x.Controls.Add(labelRow1) x.Controls.Add(inputRow1) x.Controls.Add(inputRow2)
The EDSL will now be used in the Render override to output the HTML to form the table. There will be two rows and two columns. Here’s the code:
override x.Render (writer:HtmlTextWriter) = let myTable = [ Row Cell Content (Control labelRow1) Cell Content (Control inputRow1) Row Cell Content (Static "Row 2 Static") Cell Content (Control inputRow2) ] let tedsl = new TableEDSL() tedsl.WriteTable(myTable, writer)
The “myTable” F# list contains the “statements” that comprise the EDSL “program”. They are:
- Row: Start a new table row.
- Cell: Start a new table cell in the current table row.
- Content: Output content to the current table cell. The content can be a System.Web control or static text.
Note that output of the “table” tag, and closing “tr” and “td” tags are implied in the language. Once the commands have been declared in the list “myTable”, an instance of the class “TableEDSL” is created and the method “WriteTable” executed to execute the statements in the list (“program”). This will result in the table being rendered in the web part:
Let’s now turn to how the language is created. Firstly, types are created to define what can be included in table (TableContent) and what can be included in a cell (CellContent) – these are the “statements” of the language used above:
type CellContent = | Static of string | Control of WebControl | InnerTable of TableContent list and TableContent = | Row | Cell | Content of CellContent
These are the only two types required to define the language. Note:
- These use F# discriminated union to declare what’s allowed in “CellContent” and “TableContent”.
- These types are self-referencing hence the use of “and”. Notice how a “CellContent” can contain a TableContent, and this allows a table cell to embed other tables.
- The “of” keyword declares the data type of the “statements” in the EDSL.
Now that the language is defined, a class need to be created that will interpret the language. First declare the class, some mutable values and two methods that check to see if “tr” and “td” tags need to be closed:
type TableEDSL() = let mutable haveCell = false let mutable haveRow = false member x.CheckCellClosure(writer:HtmlTextWriter) = if haveCell then writer.Write("</td>") haveCell <- false member x.CheckRowClosure(writer:HtmlTextWriter) = if haveRow then writer.Write("</tr>") haveRow <- false
The method “WriteTable” (which is called from the web part’s “Render” override) is passed the list of language “statements” and the HtmlTextWriter stream to which the HTML will be outputted:
member x.WriteTable (theTable, writer:HtmlTextWriter) = writer.Write("<table>") List.iter (fun ele -> x.Table (ele, writer)) theTable x.CheckCellClosure(writer) x.CheckRowClosure(writer) writer.Write("</table>")
- Outputs the “table” tag.
- Iterates across the list of language statements, calling the method “Table” on each statement in the list.
- Checks to see if there’s a cell to be closed through calling “CheckCellClosure”.
- Checks to see if there’s a row to be closed through calling “CheckRowClosure”
- Terminates the table.
Finally, we have the “Table” method that is called to execute each statement.
member x.Table (theTable, writer:HtmlTextWriter) = match theTable with | Row -> haveRow <- true x.CheckCellClosure(writer) writer.Write("<tr>") | Cell -> x.CheckCellClosure(writer) haveCell <- true writer.Write("<td>") | Content s -> match s with | Static str -> writer.Write(str) | Control ctl -> ctl.RenderControl(writer) | InnerTable tbl -> x.WriteTable(tbl, writer)
This method uses the “F#” match construct to provide implementations for each of the allowable statements in the language. The “Content” statement has an additional “match” to determine what type output is required. In the case of “InnerTable”, the method “Table” is called recursively.
Here’s a “program” that uses an “InnerTable”:
let myTable = [ Row Cell Content (Static "Embedded Table") Cell Content (InnerTable [ Row Cell Content (Static "Inner Table 1") Row Cell Content (Static "Inner Table 2") ]) ]
The table will be displayed as:
The language can easily be extended to, for example, allow CSS styles and other table formatting instructions to be applied to the table.
Click Here for a page showing all the source code.
F# is an interesting language, especially for creating Embedded Domain Specific Languages (EDSLs). This article by Brian McNamara on a little “Turtle Graphics” implementation with Silverlight got me thinking about how F# could be used in web parts. Anyway, the starting point is creating a web part using F#, so that’s what this blog entry is about. First, here’s the code for a “Hello World” web part:
#light namespace FSWebPart open System.Web open System.Web.UI open System.Web.UI.WebControls open System.Web.UI.WebControls.WebParts type public HelloWorldWP() = inherit WebPart() override x.Render (writer:HtmlTextWriter) = writer.Write("Hello World")
The code is completely equivalent to C# web part code: define a namespace, import namespaces, define a type (class) that inherits from “Web Part” and provide an overload for the “Render” method.
Here’s how to build and test the web part using Visual Studio 2008. It’s not quite as straightforward as with C# as the web part is not available in the toolbox. Also, this example does not sign the assembly so it’s not ready for SharePoint.
In Visual Studio 2008:
Create a new project using the F# Library project template. Call this project FSWebPart.
Add an F# Source File into which the above code should be placed. Name this file HelloWorldWP.fs. You can delete the fs file created with the project.
Build the project.
You can now add a web project to the solution which will be used to test the web part.
Add a “New Web Site” project to your solution. Call this web site WebSite.
A reference to the F# project will need to be created in this new web project. You must create a reference using the “Browse” facility to locate the assembly – do not use a project reference.
Right click the WebSite project and select Add Reference.
Click the Browse tab and select t the assembly FSWebPart.dll.
The Default.aspx page will now be modified to include a web part manager, a web part zone and to add an instance of the “HelloWorldWP” web part to the web part zone:
Open default.aspx in “source” view.
Drag and drop a WebPartManager control from the Toolbox, WebParts section, under the <body> tag in default.aspx.
Drag a WebPartZone control and drop under the </div> tag.
Add the following Register directive under the “Page” directive:
<%@ Register assembly=”FSWebPart” namespace=”FSWebPart” tagprefix=”cc2″ %>
Now add a tag in the <ZoneTemplate> tag to create an instance of our web part:
<asp:WebPartZone ID=”WebPartZone1″ runat=”server”>
<cc2:HelloWorldWP ID=”One” runat=”server” />
Compile and test the web part. It should be displayed in the ASPX page.
If you’re interested in finding out more about the F# language try these two sources:
Update: 12-May-2010: See this next post to find out how to write an EDSL for outputing Table Tags!