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.
/// 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. ArtAnonymous
October 16, 2009
Not sure if <Enter> works under Mac/FF/Silverlight. I could not get any response on the console.