Pure Danger Tech


navigation
home

CodeMash: Dustin Campbell on F#

09 Jan 2009

After doing my own talk on Actor Concurrency, I headed over to see “Multi-threading Mojo with F# ” by Dustin Campbell. And I’m glad I did! It was my favorite talk of the conference.

Due to my actor talk (for which I did some functional programming in Erlang and Scala) and what I’ve seen lately at the Lambda Lounge I’ve been waiting for a language that seemed to have the right mix of functional and imperative styles. Scala is the most obvious JVM candidate but I’m not (yet) taken with it (still withholding judgement though). So, in summary, I was very well primed to see a new functional language that runs on a VM and takes concurrency seriously….enter F#.

My initial impression is that F# combines a lot of syntax and ideas I like from Erlang but with a bit more palatable syntax. And they provide a lot of asynchronous / parallel execution support in some powerful libraries. They have an agent/actor library although it’s a bit more limited than the choices in Scala.

So, F# is (duh) a multi-paradigm .NET language. It’s statically typed but makes heavy use of type inference with optional type hints (just like Scala). It is NOT dynamic, so runs on the CLR, not the DLR.

You use “let” a lot. By default variables created with let are immutable (see val in Scala or well anything in Erlang). You can do destructuring or pattern matching (like Erlang, Scala, etc). Lists are done with []. If you want to do mutable you say “let mutable” forcing you to make that painfully obvious:

let pair = (19, 23)
let (x,y) = pair
let mutable mpair = (19, 23)
let numbers = [1..10]
let odds = [1; 3; 5; 7]
let squares = [for x in numbers -> x * x]

Functions are defined the same way almost – “let fn arg1 arg2…”:

let square x = x * x
let add x y = x + y 
let squares = List.map(fun x -> x * x) [1..10]
let squares = List.map squares [1..10]
One cool thing is the pipe operator > which lets you reorganize a set of nested function calls into a series of unixy pipes:
let sumOfSquares = List.sum(List.map square [1..10])

let sumOfSquares = [1..10] 
	|> List.map square
	|> List.sum

Love it. Makes nested functions much more natural to read.

Types are discriminated unions. Ok, I admit I don’t know what that means. But they look like this:

type Suit = | Spade | Heart | Club | Diamond
type Rank = | Ace | King | Queen | Jack | Value of int
type Card = Card of Suit * Rank

Of course, pattern matching is prevalent as expected:

let cardValue (Card(r,s)) = 
	match r with
	| Ace -> 11
	| King | Queen | JAck -> 10
	| Value(x) -> x
	
	
let oddOrEven x = 
	match x with
	| x when x % 2 = 0 -> "even"
	| _ -> "odd"

If you’ve been following along in your Erlang/Scala primers, you’ll note the use of guards (when…) and wildcard matching (_).

Then Dustin got into the concurrency stuff. I couldn’t possibly reproduce all the code or stuff he went through. He went through the incredibly hairy stuff needed to do asynchronous I/O in C# (many pages of stuff) and then showed the equivalent F# code which was (comparatively) much more readable:

let ProcessImageAsync(i) = 
	async { 
		use inStream = File.OpenRead(sprintf "Image%d.tmp" i)
		let! pixells = inStream.AsyncRead(numPixels)
		let pixels' = ProcessImage(pixels, i)
		use outStream = File.OpenWrite(sprintf "Image%d.done" i)
		do! outStream.AsyncWrite(pixels') 
	}
	
let ProcessImagesAsync() = 
	let tasks = [ for i in 1..numImages -> ProcessImageAsync(i) ]
	let parallelTasks = Async.Parallel tasks
	Async.Run parallelTasks

Note the “async” block here that looks like a keyword or a control structure – that’s actually part of the library (he didn’t go into how that kind of construct is created but it’s no doubt useful in other places too). async returns functions to be executed asynchronously later. Async.Parallel is a workflow to do the task execution in parallel. Async.Run blocks until all tasks complete (there are other options in Async as well – no wait, futures, etc). Async tasks can also hop threads while being executed.

The threads used come from the system thread pool and I assume there’s some kind of work-stealing going on there under the hood but that’s just me guessing. Someone asked about how the file resources got released and I gather when they go out of scope during execution, they are automatically disposed via IDisposable. This framework also supports exception propagation and cancellation.

Later on he gave an example of an agent library which was using a Mailbox, reading messages out of the mailbox and pattern matching, then doing tail recursion back into the agent loop. Typical actor stuff. I asked whether agents were bound to a thread or lightweight and he said they are bound to a thread in the .NET thread pool, but depending on the CLR you might get either a fiber or a real thread. So, my guess from that is that this agent library would be somewhat less scalable (similar to Scala receive actors) than a lightweight thread model with a scheduler (like Scala react, Erlang, etc). I wonder if the lack of continuation support in the CLR would also make that difficult (although of course Scala is able to manage it without continuations on the JVM).

Also, I guess the upcoming Parallel LINQ will give you more fine-grained control over managing task execution and scheduling.

In all, I thought F# looked like a very compelling language, especially if you can use it in an interoperable way with other .NET langauges on the CLR. I don’t think pure functional languages are ever going to be mainstream general-purpose programming languages. I think they’re fantastic for certain kinds of programming and give a huge boost to making scalable concurrent programs easier to write (and read). Some kind of hybrid solution is necessary. If not in the same language then on multiple languages running on the same VM technology.

If you want more info, Dustin recommended the Apress Expert F# right now or when it comes out Learning F# 1.0 by Chris Smith.