Wednesday, October 30, 2013

MonoTouch.Dialog From F#

I'm currently evaluation Xamarin as a replacement for a Phonegap application.  The first thing I'm evaluating is building UIs in code.  Since most of the example code is in C#, I initially built my test UI with it.  In poking through the Xamarin documentation I came across the MonoTouch.Dialog toolkit (hereafter MT.D).  MT.D is a declarative framework for creating iOS UIs.  Here is the code for the MasterViewController of a UISplitView (this is a minor modification to the code found in the Xamarin documentation):

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.Dialog;
using System.Linq;

namespace CSharpTabbed
{
    public partial class MasterViewController : DialogViewController {
        Section testSection;
        public MasterViewController () : base (null)
        {
            Root = new RootElement ("Items") {
                new Section(){
                    new StringElement("Section 1"),
                    from num in Enumerable.Range(1,10)
                        select new ImageElement(new UIImage("first.png"))
                },
                new Section(){
                    new StringElement("Section 2"),
                    new Section(){
                        from num in Enumerable.Range(1,10)
                            select new StringElement("Item" + num})
                    }
                },
                new Section(){
                    new StringElement("Section 3"),
                    new Section(){
                        from num in Enumerable.Range(1,10)
                            select new StringElement("Item" + num)
                    }
                }
            };
        }

        public override bool ShouldAutorotateToInterfaceOrientation
            (UIInterfaceOrientation toInterfaceOrientation)
        {
            return true;
        }
    }
}


And the resulting UI:



Since this requires the construction of a lot of collections I thought I would try it in F#.  Here is my direct conversion from C# to F#:

 1: namespace GoFormz
 2: 
 3: open System
 4: open System.Drawing
 5: open MonoTouch.Foundation
 6: open MonoTouch.UIKit
 7: open MonoTouch.Dialog
 8: open System.Linq
 9: 
10: [<Register ("MasterViewController")>]
11: type MasterViewController (window:UIWindow) as this =
12:     inherit DialogViewController (new RootElement("Items"))
13: 
14:     do this.Root.Add [new Section(Elements = new ResizeArray<Element>( [yield new StringElement("Section1") :> Element;
15:                                                                         for i in 1..10 -> new StringElement("num"+i.ToString()) :> Element]));
16:                       new Section(Elements = new ResizeArray<Element>( [yield new StringElement("Section1") :> Element;
17:                                                                         for i in 1..10 -> new StringElement("num"+i.ToString()) :> Element]));
18:                       new Section(Elements = new ResizeArray<Element>( [yield new StringElement("Section1") :> Element;
19:                                                                         for i in 1..10 -> new StringElement("num"+i.ToString()) :> Element]))];
20: 
21:     override this.ShouldAutorotateToInterfaceOrientation toInterfaceOrientation =
22:         true
namespace GoFormz
namespace System
namespace System.Drawing
namespace System.Linq
type MasterViewController =
  class
    inherit obj
    new : window:'a -> MasterViewController
    override ShouldAutorotateToInterfaceOrientation : toInterfaceOrientation:'a -> 'b
  end

Full name: GoFormz.MasterViewController
val window : 'a
val this : MasterViewController
type ResizeArray<'T> = Collections.Generic.List<'T>

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

  type: ResizeArray<'T>
  implements: Collections.Generic.IList<'T>
  implements: Collections.Generic.ICollection<'T>
  implements: seq<'T>
  implements: Collections.IList
  implements: Collections.ICollection
  implements: Collections.IEnumerable
override MasterViewController.ShouldAutorotateToInterfaceOrientation : toInterfaceOrientation:'a -> 'b

Full name: GoFormz.MasterViewController.ShouldAutorotateToInterfaceOrientation

This isn't really any better than the C# due to all the casting and the need to interface with Collections.Generics.List.  It would also be nice if I could use any collection type I want.  To address these issues, I created a library which allows more idiomatic F# usage of MT.D.  Usage looks like this:

1: do this.Root.Add [createSection [yield createStringElement "Section1";
2:                                  for i in 1..10 -> createStringElement ("num"+i.ToString())];
3:                   createSection [yield createStringElement "Section2";
4:                                  for i in 1..10 -> createStringElement ("num"+i.ToString(), "value")];
5:                   createSection [yield createStringElement "Section3";
6:                                  for i in 1..10 -> createStringElement ("num"+i.ToString(), fun ()->Console.WriteLine "test")]]


This gives compile time checking without having to use a lot of type annotations while still maintaining type safety.

In progress library here.