共用方式為


ICFP Programming Contest 2009

This year’s ICFP Programming Contest starts today.  We’ve got a team participating, any other F# teams out there? 

Last year, I posted an F# implementation of the 2006 ICFP contest problem, which was an amazing and complex set of puzzles inside a custom virtual machine.   Here’s a Silverlight version of that virtual machine, implemented on top of the Console sample I posted yesterday.

Silverlight UM – ICFP 2006

See https://boundvariable.org/ and my previous post for details of what’s running in this console.

Launch the UM

  /// Start the UM running on a background thread.
 /// The input and output functions will be called on the 
 /// background thread, and block the execution of the UM
 static member Launch(binaryUri : Uri, input : Stream, output : Stream) =
     async {
         try
 let writer = new System.IO.StreamWriter(output, AutoFlush=true)
             let reader = new System.IO.StreamReader(input)
             do writer.WriteLine("Press <enter> to begin. Note: Will download a large UMIX image.")
             do reader.ReadLine() |> ignore
             let request = System.Net.WebRequest.Create(binaryUri)
             do writer.WriteLine(binaryUri)
             let! response = request.AsyncGetResponse()
             let stream = response.GetResponseStream()
             do writer.WriteLine("Downloading...")
             let progress(percent) = writer.WriteLine("Downloaded: "+percent.ToString()+"%")
             let! bytes = asyncReadAllBytesFromStreamWithProgress (stream, int response.ContentLength, progress) 
 do writer.WriteLine("Booting...")
             let ee = UM(bytes, Func<int>(input.ReadByte), Action<byte>(output.WriteByte))
             ee.Run()
         with
 | e -> System.Diagnostics.Debug.WriteLine("UM failed with {0}", e)
     } |> Async.Start     

F# Async in Silverlight

In Silverlight, the APIs for synchronous I/O have been removed, which forces developers to do the ‘right thing’ and make async calls for long-running I/O operations, thus (hopefully) keeping the UI responsive.  This makes F# a really nice implementation language for Silverlight applications, using F#’s async workflows.  In the code above, an asynchronous workflow describes the flow of control of launching the UM.  At the two points of long running network connections, async calls are made with “let!”, so that the UI thread is not blocked and the application remains responsive.  This ultimately hides a lot of complexity that would be added in chaining these asynchronous operations together in a standard .NET approach to this.

Note also that the code above calls a helper function “asyncReadAllBytesFromStreamWithProgress”, which shows how async code in F# can be nicely factored using the same sort of “extract method” factoring you are used to in synchronous programming.  The implementation of this helper also shows that async calls with ‘let!’ can be placed inside ‘for’ and ‘while’ loops.  Chances are you don’t even want to try doing that with standard Begin/End calls!

 let internal asyncReadAllBytesFromStreamWithProgress(stream:Stream, length:int, progress:int->unit) =
    async {
        let offset = ref 0
        let count = ref length
        let buffer = Array.zeroCreate<byte> !count
        while !count > 0 do
 let! bytesRead = stream.AsyncRead(buffer, !offset, (min 524288 !count))
            if bytesRead = 0 then raise (new EndOfStreamException("Read beyond the EOF"))
            do offset := !offset + bytesRead
            do count := !count - bytesRead
            progress(100 * !offset / length)
        return buffer
    }

Consuming F# from C#+XAML

The majority of this app is written in F# – the console control is authored in F# and the application logic is in F#.  Here is the C# and XAML that ties it together:

 <UserControl xmlns:my="clr-namespace:System.Windows.Controls;assembly=SilverlightConsole" x:Class="UM.Page"
 xmlns=https://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=https://schemas.microsoft.com/winfx/2006/xaml
 Width="600" Height="400">
 <Grid x:Name="LayoutRoot" Background="White">
 <my:SilverlightConsole x:Name="console" Width="600" Height="400"
 FontFamily="Courier New" FontSize="13" FontWeight="Bold"
 Foreground="GreenYellow" Background="Black" Text="" />
 </Grid>
</UserControl>
 public partial class Page : UserControl {
    public Page() {
        InitializeComponent();
        Uri curPage = System.Windows.Application.Current.Host.Source;
        UriBuilder builder = new UriBuilder(curPage.Scheme, curPage.Host, curPage.Port, "umix.dll");
        ICFP.UM.Launch(builder.Uri, console.InputStream, console.OutputStream);
    }
}

Summary

Check out ICFP Programming Contest  2009 this weekend, and take F#+Async+Silverlight for a spin.

Comments

  • Anonymous
    August 15, 2009
    Hi Luke. Thanks. Much appreciated. Look forward to your next post along these lines. Art

  • Anonymous
    October 16, 2009
    Not sure if <Enter> works under Mac/FF/Silverlight.  I could not get any response on the console.