First, let me remind you that in my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-fourth in a infinite number of posts of "The Weekly Source Code."
F#, everyone is agog over F# and getting all functional. Again. ;) The F# September 2008 CTP came out a few weeks back and folks I'm talking to are digging it. Here's the goal:
"F# developed as a research programming language to provide the much sought-after combination of type safety, succinctness, performance, expressivity and scripting, with all the advantages of running on a high-quality, well-supported modern runtime system."
Looks like it's more than a research language as it's inside Visual Studio and looking pretty with syntax highlighting and intellisense and everything. This might be old news to you, Dear Reader, but F# is reaching a very significant level of polish. This release is moving F# to the level of being a peer of C# and VB. It's got its own F# DevCenter at MSDN. How's that for legit?
Here's a great one paragraph explanation from Vertigo's Rick Taylor about what/why/how F#:
"Unless you specify otherwise, everything in F# is immutable, much like the string construct in C#. This extends to areas that you might not expect. For instance, once you set an int to some specific value, that's it – you can't change it, unless you have marked it as mutable. The reason for this is because the language is primarily a functional one. Programs in functional languages contain functions which return values, which are then further used, etc – but each value or set of values is its own entity, and to change it while within a function is to produce a side effect, something undesirable in a functional language. In the strictest sense, functions return values but do not alter their parameters, or the outside world (which, incidentally, gives rise to the monad pattern mentioned earlier). F# follows these rules of functional programming, but also allows you to break those rules with the mutable keyword."
What's the best way to jump into F#? Well there's a few great ways. First, a little blatant self-promotion. I've done two F# podcasts with smart F# fanboys and there's great .NET Rocks and Herding Code episodes too.
- I enjoyed Robert's "Foundations of F#" book very much.
- Also, if you're a freaking ninja rocket scientist, you can always read "F# for Scientists." Seriously. I dare you to buy that, in person, at Borders. Freaking scientists. (Seriously, though, it's a fabulous book and you can get some free excerpts here)
- Don Syme, the creator of F# has "Expert F#" available and it's very highly rated.
F# Eye for the C# Guy
However, as a F# newbie with a few years of Haskell in the back of my head, the presentation that has clicked with me the most was Leon's F# eye for the C# guy PowerPoint. Scandalous, I know, as you may know Leon as the cruel bully who called me Hanselgirl in public recently at TechEd Australia. I promptly boxed his ears (it was more of a slap-fight actually) and there was also a public arm-wrestling. Between the two of us I'm sure there was the potential to splice together one normal-sized male arm, but I digress. You can download his deck here, but I've also taken the liberty to put it on SlideShare and embedded it here.
I had intellectualized slides 42 through 45, but the way he described it clicked. He did what I would do:
"I worked this out using Reflector. I did not work this out from reading books or papers or listening to podcasts or quizzing intelligent people. A simple let statement is a static function, under the hood. When you see let, think function."
Check out those few slides,. It's a great deck, and I'm sure Leon wouldn't mind YOU, Dear Reader, showing his talk at your local User Group or having a Programmer's Lunch at work and taking complete credit for it. That's what I fully plan on doing. ;) Suck it Bambrick!*
Let's see three bits of code.
First, something basic.
Remember there are no variables. No side-effects.
let sqr x = x * x
let multiply x y = x * y
print_int (sqr 3)
print_int (multiply 3 4)
// recursive function fibonacci series using pattern matching
let rec fib x =
match x with
| x when x <= 0 -> failwith "An integer greater than 0 is required."
| 1 -> 1
| 2 -> 1
| x -> fib (x - 1) + fib (x - 2)
print_int (fib 15)
// functions as values
let add x y = x + y
let a1 = add 3
let a2 = a1 4
Second, something shiny.
Units of Measure in F#. What? Andrew Kennedy's PhD thesis from 13 years ago just happens to be a feature in F#. He's done a three part series:
Basically they've added static checking and inferences for units-of-measure. Not any specific ones, but the concept itself.
"As far as F# is concerned, ft and m have nothing to do with each other. It's up to you, the programmer, to define appropriate conversion factors."
Here's a screenshot from his blog that says a thousand words and showcases the FSharp.Math.PhysicalConstants namespace, along with the International System of Units (SI) namespace.
Also from Andrew's blog:
"You can define mutually recursive measures using "and" to connect them and placing the Measure attribute immediately before the name of the measure:"
type [<Measure>] km =
static member toM = 1.0/1000.0<m/km>
and [<Measure>] m =
static member toKm = 1000.0<km/m>
Third, something silly.
A fun little 2D Tron-Clone game from last year, written in only 182 lines of F# by Phil Trelford. You can download it from hubFS and read how he wrote it on the Applied Games Group Blog. The game even supports Xbox 360 controllers! Sweet.
Here's all 182 lines, not counting the first 4 lines of comments.
// LightCycles.fs Mini game using windows forms
// 2007 written by Phillip Trelford
#R @"C:\WINDOWS\assembly\GAC_32\Microsoft.DirectX\220.127.116.11__31bf3856ad364e35\Microsoft.DirectX.dll" // Feb 206
open Microsoft.DirectX.XInput // Required to read XBox 360 controllers
/// Game states
type GameState = | Start | Play | Over
/// Form key handler type
type KeyHandler (form:Form) =
do form.KeyPreview <- true
let keys = Enum.GetValues (type Keys) :?> (Keys )
let keysDown = Array.create keys.Length false
let FindKeyIndex code = keys |> Array.find_index (fun x -> code = x)
do form.KeyDown.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- true)
do form.KeyUp.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- false)
member this.IsKeyDown (keyCode:Keys) = keysDown.[FindKeyIndex keyCode]
member this.AnyKeyDown () = keysDown |> Array.exists (fun x -> x)
/// Player direction type
type Direction = | Left | Right | Up | Down
/// Player type
type Player (color,startX,startY,direction,keys,keyHandler:KeyHandler) =
let mutable x = startX
let mutable y = startY
let mutable d = direction
member this.Color = color
member this.X = x
member this.Y = y
member this.Keys = keys
/// Reset player to start values
member this.Reset () = x <- startX; y <- startY; d <- direction
/// Updates player position
member this.Update i =
// Read keyborad
let mutable newD = d
let up, down, left, right = keys
if keyHandler.IsKeyDown(up) then newD <- Up
if keyHandler.IsKeyDown(down) then newD <- Down
if keyHandler.IsKeyDown(left) then newD <- Left
if keyHandler.IsKeyDown(right) then newD <- Right
// Read XBox 360 controller
let state = Controller.GetState(i)
if state.IsConnected then
let pad = state.GamePad
if pad.UpButton then newD <- Up
if pad.DownButton then newD <- Down
if pad.LeftButton then newD <- Left
if pad.RightButton then newD <- Right
/// Don't allow suicide move
match (d,newD) with
| (Left, Right) | (Right, Left) | (Up, Down) | (Down, Up) -> ()
| _ -> d <- newD
/// Update position with direction
match d with
| Up -> y <- y - 1
| Down -> y <- y + 1
| Left -> x <- x - 1
| Right -> x <- x + 1
/// Main form
let form = new Form (Text="Light Cycles", Width=680, Height=544)
do /// Layout for game window and status panel
let layout = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount = 2)
layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Percent, Width = 100.0f ) ) |> ignore
layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Absolute, Width = 128.0f) ) |> ignore
/// Play area in pixels
let playArea = 500
/// Game play area bitmap
let bm = new Bitmap(playArea, playArea)
/// Clears screen
let ClearScreen () =
using (Graphics.FromImage(bm)) (fun graphics -> graphics.Clear(Color.Black))
/// Draws text to screen
let DrawText s =
using (Graphics.FromImage(bm)) (fun graphics ->
let rect = new RectangleF(0.0f,0.0f,float32 playArea,float32 playArea)
let align = new StringFormat(Alignment=StringAlignment.Center, LineAlignment=StringAlignment.Center)
graphics.DrawString(s, form.Font, Brushes.White, rect, align)
// Initialise screen
DrawText "Press any key to start"
/// PictureBox to contain game bitmap
let pictureBox = new PictureBox(Dock=DockStyle.Fill)
pictureBox.Image <- bm
let keyHandler = KeyHandler (form)
/// Players array
let players =
[| Player (Color.Red,playArea/2+20,playArea/2,Down,(Keys.Q,Keys.A,Keys.Z,Keys.X),keyHandler);
Player (Color.LightBlue,playArea/2-20,playArea/2,Up,(Keys.P,Keys.L,Keys.N,Keys.M),keyHandler) |]
players |> Array.iter (fun player -> bm.SetPixel(player.X,player.Y,player.Color))
/// Display player controls
let statusPanel = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount=1, BackColor=Color.DarkGray)
players |> Array.iteri (fun i player ->
let name =
[| ((new Label (Text=sprintf "Player %d" i, ForeColor=player.Color)) :> Control) |]
let up, down, left, right = player.Keys
let controls =
Array.combine [|"Up";"Down";"Left";"Right"|] [|up;down;left;right|]
|> Array.map (fun (name,key) -> (new Label (Text=sprintf "%s '%O'" name key)) :> Control )
Array.append name controls
/// Game play - returns true if there has been a collision otherwise false
let PlayGame () =
let collisions = players |> Array.mapi (fun i player ->
let x, y = (player.X, player.Y)
let wall = x < 0 || x >= playArea || y < 0 || y >= playArea
if wall then
let bgColor = bm.GetPixel(x, y)
bm.SetPixel (x, y, player.Color)
players |> Array.exists (fun player -> let c = player.Color in c.R = bgColor.R && c.G = bgColor.G && c.B = bgColor.B )
match collisions |> Array.tryfind_index (fun x -> x = true) with
| Some(i) -> i
| None -> (-1)
/// Current game state
let gameState = ref GameState.Start
let gameOverWaitCount = ref 200
let r = new Random()
/// Timer instance
let timer = new Timer()
timer.Interval <- 1000/50
// Timer event
timer.Tick.Add (fun _ ->
match !gameState with
| Start ->
if keyHandler.AnyKeyDown () then
gameState := GameState.Play
| Play ->
let i = PlayGame ()
if i>=0 then
gameState := GameState.Over
gameOverWaitCount := 200
DrawText (sprintf "Game Over - Play %d Lost" i)
| Over ->
// Shake screen
form.Left <- form.Left + if !gameOverWaitCount > 150 then r.Next(5) - 2 else 0
// Decrement Game Over wait
if !gameOverWaitCount <= 0 then
gameState := GameState.Start
players |> Array.iter (fun player -> player.Reset ())
DrawText "Press any key to start"
Try to learn ONE new language each year!
* Oh, relax, Dear Reader. Leon and I are, like, totally, BFF. We're peas and carrots. Truly. The bastard.