10
\$\begingroup\$

At work I am developing an application using hand-coded Swing, and I've found that I have an easier time reading, writing, and maintaining hierarchical component creation using code blocks like:

    JPanel mainPanel = new JPanel(new BorderLayout());
    {
        JLabel centerLabel = new JLabel();
        centerLabel.setText("Hello World");
        mainPanel.add(centerLabel, BorderLayout.CENTER);
    }
    {
        JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0,0));
        {
            JLabel label1 = new JLabel();
            label1.setText("Hello");
            southPanel.add(label1);
        }
        {
            JLabel label2 = new JLabel();
            label2.setText("World");
            southPanel.add(label2);
        }
        mainPanel.add(southPanel, BorderLayout.SOUTH);
    }

So I wondered what that would look like using F# and WinForms, and I translated this code example (go to "Browse Code" tab, then click on F# -> editor.fsx in tree view) in two ways and I'd like feedback on which of the following two ways seems better. The first uses code blocks like the approach shown in Swing, and I think it is nice but a bit cluttered with begin ... end everywhere:

open System
open System.Windows.Forms

let form = new Form()
form.Width  <- 400
form.Height <- 300
form.Visible <- true 
form.Text <- "Hello World Form"
begin
    // Menu bar, menus 
    let mMain = new MainMenu()
    begin 
        let mFile = new MenuItem("&File")
        begin                
            let miQuit  = new MenuItem("&Quit")
            miQuit.Click.Add(fun _ -> form.Close())
            mFile.MenuItems.Add(miQuit) |> ignore
        end
        mMain.MenuItems.Add(mFile) |> ignore
    end
    form.Menu <- mMain
end
begin
    // RichTextView 
    let textB = new RichTextBox()
    textB.Dock <- DockStyle.Fill  
    textB.Text <- "Hello World\n\nOK."
    form.Controls.Add(textB)
end

The second approach is interesting because it takes advantage of the fact that in F# everything is an expression, but it seems a little harder for me to follow and I'm not sure if that's just because I'm used to the code block approach:

open System
open System.Windows.Forms

let form = new Form()
form.Width  <- 400
form.Height <- 300
form.Visible <- true 
form.Text <- "Hello World Form"

form.Menu <-
    // Menu bar, menus 
    let mMain = new MainMenu()
    mMain.MenuItems.Add(
        let mFile = new MenuItem("&File")
        mFile.MenuItems.Add(
            let miQuit  = new MenuItem("&Quit")
            miQuit.Click.Add(fun _ -> form.Close())
            miQuit
        ) |> ignore
        mFile
    ) |> ignore
    mMain

form.Controls.Add(
    // RichTextView 
    let textB = new RichTextBox()
    textB.Dock <- DockStyle.Fill  
    textB.Text <- "Hello World\n\nOK."
    textB
)

Or maybe someone can suggest some other approach for structuring F# + WinForms code which is in this same spirit (i.e. emphasizing the component hierarchy both visually and in limiting scope).

Update

I recently learned that in F#, begin ... end and ( ... ) are interchangeable. Which means that my concern with the first example is eliminated since I can write:

open System
open System.Windows.Forms

let form = new Form()
form.Width  <- 400
form.Height <- 300
form.Visible <- true 
form.Text <- "Hello World Form"
(
    // Menu bar, menus 
    let mMain = new MainMenu()
    (
        let mFile = new MenuItem("&File")
        (
            let miQuit  = new MenuItem("&Quit")
            miQuit.Click.Add(fun _ -> form.Close())
            mFile.MenuItems.Add(miQuit) |> ignore
        )
        mMain.MenuItems.Add(mFile) |> ignore
    )
    form.Menu <- mMain
)
(
    // RichTextView 
    let textB = new RichTextBox()
    textB.Dock <- DockStyle.Fill  
    textB.Text <- "Hello World\n\nOK."
    form.Controls.Add(textB)
)

And indeed, the second example could be written as

open System
open System.Windows.Forms

let form = new Form()
form.Width  <- 400
form.Height <- 300
form.Visible <- true 
form.Text <- "Hello World Form"

form.Menu <-
    // Menu bar, menus 
    let mMain = new MainMenu()
    mMain.MenuItems.Add begin
        let mFile = new MenuItem("&File")
        mFile.MenuItems.Add begin
            let miQuit  = new MenuItem("&Quit")
            miQuit.Click.Add(fun _ -> form.Close())
            miQuit
         end |> ignore
        mFile
    end |> ignore
    mMain

form.Controls.Add begin
    // RichTextView 
    let textB = new RichTextBox()
    textB.Dock <- DockStyle.Fill  
    textB.Text <- "Hello World\n\nOK."
    textB
end

So syntax isn't an issue anymore, but there still is a difference in style.

\$\endgroup\$
  • \$\begingroup\$ I'd argue it looks difficult to follow because of the way you're using F# - WinForms certainly isn't its choice arena, and while there may be coincidental conveniences, it is inherently less concise for this purpose than the variety of .NET languages available that had UI in mind but mostly providing a generally explanatory syntax, a little like English. \$\endgroup\$ – Grant Thomas Apr 11 '11 at 10:37
  • 1
    \$\begingroup\$ @Mr. Disappointment: I respectfully disagree. It may not be obvious how to do it at first, but declaring hierarchies of objects in F# benefits from nested declarations. C-like languages like C# allow it only through blocks, resulting in a verbose use of braces. \$\endgroup\$ – Joh Jun 8 '11 at 8:00
4
\$\begingroup\$

Here is my take on your code:

open System
open System.Windows.Forms

let form =
    new Form(
        Width = 400,
        Height = 300,
        Visible = true,
        Text = "Hello World Form")

// Menu bar, menus 
let mMain = 
    let miQuit  = new MenuItem("&Quit")
    miQuit.Click.Add(fun _ -> form.Close())

    let mFile = new MenuItem("&File")
    mFile.MenuItems.Add(miQuit) |> ignore

    let mMain = new MainMenu()
    mMain.MenuItems.Add(mFile) |> ignore

    mMain

form.Menu <- mMain

// RichTextView 
let textB = new RichTextBox(Dock = DockStyle.Fill, Text = "Hello World\n\nOK.")

form.Controls.Add(textB)

I tried to improve by assigning properties directly in the constructor call. I also used nested let declarations to reflect the hierarchy. Finally, I used indentation instead of explicit begin/end or parens to delimit blocks.

Note also how I used mMain within the declaration of the top-level mMain. Others might have used tmp, I think using the same name makes the intent clear. It might be a bit confusing for new-comers, though.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.