Inside F#

Brian's thoughts on F# and .NET

Archive for April, 2010

Game programming in F# (with Silverlight and WPF)

Posted by Brian on April 24, 2010

Shall we play a game?

I wanted to learn more about Silverlight, WPF, and Xaml (in the context of F#, of course), so I wrote a fun little game you can play in your browser (or on your desktop).  It’s a knock-off of a dozen other similar games, as you can probably tell from this screenshot:

SuperBreakAwayScreenshot

What you don’t get from the screenshot are the amusing sound effects or the fun game play, so why not go try it out in your browser right now?  I’ll still be here when you return. (Don’t forget to come back and keep reading!)

<waits patiently for your return>

It’s kinda fun, right?  I was pleased with it, especially given that it is just the second Silverlight app I ever wrote (you might recall the first, here), and it’s also my first real foray into interactive game programming.

How I wrote it

I really had little clue what I was doing.

I had some pictures sketched out on paper of what I wanted the app to look like.  But I have little experience with WPF, and so I spent a bit of time looking up APIs for things like how to draw a circle on the screen, or a line.  The basic math for keeping track of ball velocities and bouncing off walls is pretty straightforward, so I coded that all up.  I had no idea how it would perform in real time, so I did the simplest thing – I wrote a game loop that just updates the state of the world every 20 milliseconds.  Of course I used F# async to simplify authoring the UI logic and reacting to keyboard/mouse events.  But I kept it as simple as possible; all the code runs on the UI thread.

By the end of an evening I had the basic game app working, which amazed me, as I had no idea how long it might take.  Despite blundering through new-to-me Silverlight and WPF APIs and having to read lots of docs, I had a playable game in less than six hours of coding.

It was running a little slow, so the next time I sat down with it, I added some “#if SILVERLIGHT” bits to the code so that I compile it as either a desktop app or a Silverlight app.  Maybe there is a way to profile Silverlight code, I dunno, but I do already know how to use Visual Studio’s profiler on desktop apps.  So I put the desktop version through the profiler, found one place where I was hemorrhaging memory (creating lots of extra work for the garbage collector), fixed that (with one line of code), and then the app was pretty snappy.  This pleased me to no end; you can have 3200 WPF objects moving around on the screen in a simple game loop, and it still runs smoothly, even in Silverlight, in the browser, on a laptop.  I’d really thought I was going to have to do a lot of work to get good runtime performance out of the game I had envisioned, but in fact I just did the most straightforward thing and the perf was already good enough.  WPF and Silverlight do not suck!

Since then I’ve done a little more work: some refactoring and cleanup, as well as adding the sound effects (I searched the web for public-domain sounds and picked four that I liked).  I also added the startup screen and the pause button.  There is a lot more I might like to do with the app (add scoring, different levels, different fun aspects of the game play), but my teammates on the F# team have pointed out that it’s already a nice “F# sample app” and I should just go ahead and publish it already.  So here we are.

Did I mention it’s only 400 lines of code?

It’s only 400 lines of code.  And it’s pure F#.  Actually, those are both lies; there are 373 lines of F# code, and 20 lines of Xaml.  So it’s less than 400 lines total, and not quite pure F# because I wanted to try out Xaml and so I coded a little in Xaml to learn a little more about that.

The source code

The full source code is below, for those who just want to read it here in the blog post.  I also zipped up the Visual Studio 2010 solution, which you can download from this page.  The solution contains two projects, the F# WPF app and the F# Silverlight app, that compile from the same source code.  (Note that if you try to run the Silverlight project using ‘F5’ in Visual Studio it will not work; it will open the browser to the ‘bin’ directory, rather than ‘bin\Debug’ or ‘bin\Release’, due to a product bug.  But you can just build the project, and then manually open the generated test page HTML in your browser.  Also, sometimes when I open the Xaml file in the designer, it gives me an error, but then if I reload the file again, it works.  (There is a reason we did not ship an F# “Silverlight Application” template in the box – we still have a number of little tooling bugs we need to fix.)  If you want to do things on your own, I’d recommend creating a “C# Silverlight Application” project that uses an “F# Silverlight Library” project, for a smoother tooling experience inside Visual Studio.  But I was stubborn and wanted only F#, so I did this.  Anyway…)

Here’s the little bit of Xaml:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            Name="mainPanel">
    <Popup Name="popup">
        <Canvas Name="popupCanvas">
            <TextBox Name="popupTop" FontSize="18" Canvas.Left="20" Canvas.Top="20" />
            <TextBox Name="popupMiddle" FontSize="14" Canvas.Left="20" Canvas.Top="60" />
            <TextBox Name="popupBottom" FontSize="18" Canvas.Left="20" Canvas.Top="200" />
        </Canvas>
    </Popup>
    <Border BorderThickness="15.0" BorderBrush="Black">
    <StackPanel Name="stackPanel1">
        <TextBlock Text="Super BreakAway!" FontSize="24" HorizontalAlignment="Center" />
        <TextBlock Text="written in F#, by Brian McNamara - press 'p' to pause" FontSize="12" HorizontalAlignment="Center" />
        <Border BorderThickness="2.0" BorderBrush="Black">
            <Canvas Name="canvas" Background="White" />
        </Border>
    </StackPanel>
</Border>
</StackPanel>

and here’s the F# code:

namespace Module1

open System.Windows 
open System.Windows.Shapes 
open System.Windows.Controls  
open System.Windows.Controls.Primitives 
open System.Windows.Media 

module WPFExtensions =
    module Canvas =
        let SetTopLeft(element, top, left) =
            Canvas.SetTop(element, top)
            Canvas.SetLeft(element, left)
    type System.Windows.Controls.Canvas with
        member this.AddAt(top, left, element) =
            Canvas.SetTopLeft(element,top,left)
            this.Children.Add(element) |> ignore
    let WHITE = new SolidColorBrush(Colors.White)
    let GRAY = new SolidColorBrush(Colors.Gray)
    let BLACK = new SolidColorBrush(Colors.Black)
open WPFExtensions

module GLOBAL_CONFIGURABLE_CONSTANTS =
    let E = 0.0001
    // pixel size of a ball/brick
    let SIZE = 6.0
    // initial grid size of blocks
    let WIDTH = 80
    let HEIGHT = 20
    // paddle size
    let PADHEIGHT = 10.0
    let PADWIDTH = 8.0 * SIZE
    let CHEAT = false
open GLOBAL_CONFIGURABLE_CONSTANTS

module GLOBAL_COMPUTED_CONSTANTS =
    let HALFSIZE = SIZE / 2.0
    // pixel location of bottom of bricks
    let BOTBRICKS = float HEIGHT * SIZE
    // canvas size
    let CANWIDTH=SIZE * float WIDTH
    let CANHEIGHT=SIZE * 80.0
    // pixel location of top of paddle
    let TOPPAD = CANHEIGHT-60.0
    let HALFPADWIDTH = PADWIDTH / 2.0
open GLOBAL_COMPUTED_CONSTANTS

module GLOBALS =
    let loadXaml<'T when 'T :> FrameworkElement>(xamlPath) =  
        use stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(xamlPath) // if BuildAction=EmbeddedResource
#if SILVERLIGHT
        let stream = (new System.IO.StreamReader(stream)).ReadToEnd()
#endif
        let xaml = System.Windows.Markup.XamlReader.Load(stream) 
        let uiObj = xaml :?> 'T
        uiObj

    let (?) (fe:FrameworkElement) name : 'T =
        fe.FindName(name) :?> 'T
       
    let mainPanel : StackPanel = loadXaml("MainWindow.xaml")
    let canvas : Canvas = mainPanel?canvas
    let popup : Popup = mainPanel?popup
    let popupCanvas : Canvas = mainPanel?popupCanvas
    let popupTop : TextBox = mainPanel?popupTop
    let popupMiddle : TextBox = mainPanel?popupMiddle
    let popupBottom : TextBox = mainPanel?popupBottom
    // Note that making these module-scoped globals gets rid of lots of the codegen-closure-costs of referencing them from inside an async.
    // I have not profiled, but looking at the codegen in Reflector, all the little closure classes are much smaller when these guys are globals.
    // So I am presuming this may help perf.  (But then I tried profiling, and changing it back does not seem to matter much.)
    let RNG = new System.Random()
    // main data objects
    let mutable remaining = WIDTH * HEIGHT
    let mutable active = 1
    let mutable wantPaddleBeep = false
    let mutable wantBlockBeep = false
    let mutable firstTime = true
    // main UI objects
    let tb = new TextBlock(Height=25.0, Width=CANWIDTH, Text="", FontSize=20.0)
    let debug = new TextBlock(Height=25.0, Width=CANWIDTH, Text="", FontSize=10.0)
    let paddle = new Rectangle(Width=PADWIDTH, Height=PADHEIGHT, Fill=BLACK)
    /// as n varies from 0-max-1, this makes a pretty color spectrum
    let makeColor(n,max) =
        if n < 1*max/4 then
            let px = (n-0*max/4)*256*4/max
            new SolidColorBrush(Color.FromArgb(0xFFuy,0xFFuy,byte(px),0uy))
        elif n < 2*max/4 then
            let px = (n-1*max/4)*256*4/max
            new SolidColorBrush(Color.FromArgb(0xFFuy,0xFFuy-byte(px),0xFFuy,0uy))
        elif n < 3*max/4 then
            let px = (n-2*max/4)*256*4/max
            new SolidColorBrush(Color.FromArgb(0xFFuy,0uy,0xFFuy-byte(px),byte(px)))
        else 
            let px = (n-3*max/4)*256*4/max
            new SolidColorBrush(Color.FromArgb(0xFFuy,byte(px),0uy,0xFFuy))

    let NUMBEEP = 20
#if SILVERLIGHT
    let makeMedia(file) = new MediaElement(Source = new System.Uri(file, System.UriKind.Relative), AutoPlay = false)
#else
    let makeMedia(file) = new MediaElement(Source = new System.Uri(file, System.UriKind.Relative), LoadedBehavior = MediaState.Manual)
#endif

    let attachMedia(file) =
        let sound = makeMedia(file)
        sound.MediaFailed.Add (fun ea -> debug.Text <- ea.ErrorException.ToString())
        canvas.Children.Add(sound) |> ignore
        sound

    let beeps = Array.init NUMBEEP (fun _ -> attachMedia("BEEPPURE.wma"))
    let mutable curBeep = 0
    let blockBeeps = Array.init NUMBEEP (fun _ -> attachMedia("BEEPDOUB.wma"))
    let winSound = attachMedia("happykids.wma")
    let loseSound = attachMedia("boooo.wma")
    let playOnce(sound : MediaElement) =
        async { sound.Play()
                let! _ = Async.AwaitEvent sound.MediaEnded 
                sound.Stop() } |> Async.StartImmediate 
    // useful functions
    let Assert(b) =
        assert(b)
        //if not b then raise <| new System.Exception("assert failed")

    // screen coordinates, a ball hit a block (filling space [0-SIZE,0-SIZE]) at point
    // (x,y) with velocity (dx,dy) - did it hit the side of the brick (as opposed to top/bottom)?
    let hitSide(x,y,dx,dy) =
        let ballSlope = -dy/dx
        if dy>0.0 then 
            if dx<0.0 then
                // it's going 'down-left'
                let s = y/(SIZE-x)
                ballSlope < s
            else
                // it's going 'down-right'
                let s = -y/x
                ballSlope > s
        else
            if dx>0.0 then
                // it's going 'up-right'
                let s = (SIZE-y)/x
                ballSlope < s
            else
                // it's going 'up-left'
                let s = -(SIZE-y)/(SIZE-x)
                ballSlope > s
    let _ok =
        Assert(hitSide(HALFSIZE,HALFSIZE,10.0,1.0))             // -
        Assert(hitSide(HALFSIZE,HALFSIZE,10.0,-1.0))            // -
        Assert(not<|hitSide(HALFSIZE,HALFSIZE,1.0,-10.0))       // |
        Assert(not<|hitSide(HALFSIZE,HALFSIZE,-1.0,-10.0))      // |
        Assert(hitSide(HALFSIZE,HALFSIZE,-10.0,-1.0))           // -
        Assert(hitSide(HALFSIZE,HALFSIZE,-10.0,1.0))            // -
        Assert(not<|hitSide(HALFSIZE,HALFSIZE,-1.0,10.0))       // |
        Assert(not<|hitSide(HALFSIZE,HALFSIZE,1.0,10.0))        // |

    let ensureNonZero x = if x=0.0 then E else x
open GLOBALS

[<RequireQualifiedAccess>]
type BlockState =
    | InitialPosition       // in block rows at top
    | Active                // a ball, moving around
    | Removed               // fell off bottom

type Block(shape : Ellipse) =
    let mutable state = BlockState.InitialPosition
    // next 3 fields only matter when state=Active
    let mutable xSpeed = 0.0
    let mutable ySpeed = 0.0
    let mutable tail : Line = null
    do Assert(canvas.Children.Contains(shape)) 
    member this.State = state
    member this.Shape = shape
    member this.Reflect() =
        ySpeed <-  -abs(ySpeed)
    member this.Remove() =
        Assert(state = BlockState.Active)
        canvas.Children.Remove(shape) |> ignore
        canvas.Children.Remove(tail) |> ignore
        state <- BlockState.Removed 
    member this.BreakAway() =
        Assert(state = BlockState.InitialPosition)
        xSpeed <- ensureNonZero(SIZE * (RNG.NextDouble() - 0.5))
        ySpeed <- SIZE * (RNG.NextDouble() + 2.0)/3.1  // trying to ensure ySpeed < SIZE, so ball never goes completely through a row undetected in a single 'step'
        Canvas.SetTop(shape, Canvas.GetTop(shape)+SIZE*1.5)
        tail <- new Line(X1=Canvas.GetLeft(shape), X2=Canvas.GetLeft(shape), 
                         Y1=Canvas.GetTop(shape), Y2=Canvas.GetTop(shape), 
                         StrokeThickness=SIZE/3.0, Stroke=GRAY)
        canvas.Children.Add(tail) |> ignore
        state <- BlockState.Active 
    member this.MoveOneStep() =
        Assert(state = BlockState.Active)
        let origCenteredX = Canvas.GetLeft(shape) + HALFSIZE
        let origCenteredY = Canvas.GetTop(shape) + HALFSIZE
        // compute new X
        let newX = xSpeed + Canvas.GetLeft(shape)
        let flipX(r) = xSpeed <-  -xSpeed; r
        let newX = if newX < 0.0 then flipX 0.0 else newX
        let newX = if newX > CANWIDTH-E then flipX(CANWIDTH-E) else newX
        // compute new Y
        let newY = ySpeed + Canvas.GetTop(shape)
        let flipY(r) = ySpeed <-  -ySpeed; r
        let newY = if newY < 0.0 then flipY 0.0 else newY
        // update position
        Canvas.SetTopLeft(shape, newY, newX)
        // update trailer line
        let newCenteredX = Canvas.GetLeft(shape) + HALFSIZE
        let newCenteredY = Canvas.GetTop(shape) + HALFSIZE
        tail.X2 <- newCenteredX
        tail.Y2 <- newCenteredY
        tail.X1 <- 4.0 * (origCenteredX - newCenteredX) + newCenteredX
        tail.Y1 <- 4.0 * (origCenteredY - newCenteredY) + newCenteredY
    member this.HitPaddle(dx) =
        Assert(state = BlockState.Active)
        ySpeed  <-  -abs(ySpeed)
        xSpeed <- ensureNonZero(xSpeed + dx)
    member this.ReboundOffBrick(dLeft, dTop) =
        let side = hitSide(dLeft,dTop,xSpeed,ySpeed)
        if side then 
            xSpeed <-  -xSpeed
        else
            ySpeed <-  -ySpeed

type MyApp() as this =
#if SILVERLIGHT
    inherit Application()
#else
    inherit Window()
#endif
    let cc = new ContentControl()
    let blocks = Array2D.init HEIGHT WIDTH (fun y x -> 
        let e = new Ellipse(Width=SIZE, Height=SIZE, Fill=makeColor(x,WIDTH))
        canvas.AddAt(SIZE * float y, SIZE * float x, e)
        new Block(e))

    do
        canvas.Width <- CANWIDTH; canvas.Height <- CANHEIGHT 
        canvas.AddAt(TOPPAD, CANWIDTH / 2.0, paddle)
        canvas.AddAt(TOPPAD+PADHEIGHT+5.0, 10.0, tb)
        canvas.AddAt(TOPPAD+PADHEIGHT+30.0, 10.0, debug)

        popupCanvas.Background <- new SolidColorBrush(Color.FromArgb(0xFFuy,0uy,0uy,0xFFuy), Opacity=0.6)
        popup.HorizontalAlignment <- HorizontalAlignment.Left 
        popup.VerticalAlignment <- VerticalAlignment.Top 
#if SILVERLIGHT
        // Silverlight popups are relative to the whole control
#else
        // WPF popups have more control
        popup.Placement <- PlacementMode.Relative 
        popup.PlacementTarget <- mainPanel
        popup.HorizontalOffset <- 0.0
        popup.VerticalOffset <- 0.0
#endif

        blocks.[HEIGHT-1,WIDTH/2].BreakAway()
        remaining <- remaining - 1
        tb.Text <- sprintf "%d bricks remain, %d balls active" remaining active

#if SILVERLIGHT
        this.UnhandledException.Add(fun ea -> debug.Text <- ea.ExceptionObject.ToString())
        this.Startup.Add(fun _ ->
#else
        this.Loaded.Add(fun _ ->
#endif
            async {
                do! Async.Sleep(50)  // a hack, need to wait until ActualHeight is populated
                popupCanvas.Height <- mainPanel.ActualHeight 
                popupCanvas.Width <- mainPanel.ActualWidth 
                popup.IsOpen <- true
                popupTop.Text <- "Welcome to Super BreakAway!"
                popupTop.HorizontalAlignment <- HorizontalAlignment.Center  // TODO cannot seem to auto-align these; design-time issue?  recompute layout?
                popupMiddle.Text <- "Move the mouse to control the paddle\nPrevent balls from falling off bottom\nBreak bricks on top to release more\nHave fun!"
                popupBottom.Text <- "Press 'p' to start"
            } |> Async.StartImmediate 
            async { 
                do! Async.Sleep(100)
                while remaining > 0 && active > 0 do
                    do! Async.Sleep(20)
                    do  // this 'do' line is important to memory performance - code below is all sync, so need to execute outside 'async' to avoid Async allocating
                        if popup.IsOpen then () else
                        wantPaddleBeep <- false
                        wantBlockBeep <- false
                        curBeep <- (curBeep + 1) % NUMBEEP
                        let leftPad = Canvas.GetLeft(paddle)
                        for y in 0..HEIGHT-1 do
                            for x in 0..WIDTH-1 do
                                let ball = blocks.[y,x]
                                if ball.State = BlockState.Active then
                                    ball.MoveOneStep()
                                    let top = Canvas.GetTop(ball.Shape)
                                    let left = Canvas.GetLeft(ball.Shape)
                                    if top >= TOPPAD && top < TOPPAD+PADHEIGHT && left >= leftPad && left < leftPad+PADWIDTH then
                                        // hit paddle
                                        ball.HitPaddle(dx=(left - leftPad - HALFPADWIDTH)/HALFPADWIDTH)
                                        wantPaddleBeep <- true
                                    elif top < BOTBRICKS then
                                        // see if hit a stationary brick
                                        let brick = blocks.[int(top / SIZE),int(left / SIZE)]
                                        if brick.State = BlockState.InitialPosition  then
                                            let t = Canvas.GetTop(brick.Shape)
                                            let l = Canvas.GetLeft(brick.Shape)
                                            let intersect = left >= l && left < l+SIZE && top >= t && top < t+SIZE
                                            if intersect then
                                                remaining <- remaining - 1
                                                active <- active + 1
                                                tb.Text <- sprintf "%d bricks remain, %d balls active" remaining active
                                                ball.ReboundOffBrick(dLeft=l-left, dTop=t-top)
                                                brick.BreakAway()
                                                wantBlockBeep <- true
                                    elif top > CANHEIGHT then
                                        // fell off bottom
                                        if CHEAT then
                                            ball.Reflect()
                                        else
                                            ball.Remove()
                                            active <- active - 1
                                            tb.Text <- sprintf "%d bricks remain, %d balls active" remaining active
                        if wantPaddleBeep then
                            playOnce(beeps.[curBeep])
                        if wantBlockBeep then
                            playOnce(blockBeeps.[curBeep])
                if remaining > 0 then
                    tb.Text <- sprintf "left %d bricks" remaining
                    playOnce(loseSound)
                else
                    tb.Text  <- "You Win!!!"
                    playOnce(winSound)
            } |> Async.StartImmediate 
        )

        // to be able to get focus
        cc.IsTabStop <- true
        cc.IsEnabled <- true
        cc.KeyDown.Add(fun keyEA ->
            if keyEA.Key = Input.Key.P then
                popupCanvas.Height <- mainPanel.ActualHeight 
                popupCanvas.Width <- mainPanel.ActualWidth 
                popup.IsOpen <- not popup.IsOpen
                popupTop.Text <- "PAUSE"
                popupMiddle.Text <- "F# - 'fun' is our keyword!"
                popupBottom.Text <- "Press 'p' to unpause and continue"
        )
#if SILVERLIGHT            
        System.Windows.Browser.HtmlPage.Plugin.Focus()
#else
        cc.Focus() |> ignore
#endif
        mainPanel.MouseMove 
        |> Observable.add (fun ea -> 
            let x = ea.GetPosition(canvas).X
            if x < HALFPADWIDTH then
                Canvas.SetLeft(paddle, 0.0)
            elif x <= CANWIDTH - HALFPADWIDTH then
                Canvas.SetLeft(paddle, x - HALFPADWIDTH)
            else 
                Canvas.SetLeft(paddle, CANWIDTH - PADWIDTH)
        )
        cc.Content <- mainPanel
#if SILVERLIGHT
        this.RootVisual <- cc
#else
        this.Content <- cc
        this.SizeToContent <- SizeToContent.WidthAndHeight 
#endif

#if SILVERLIGHT
#else
module Main =
    [<System.STAThread()>] 
    do  
        let app =  new Application() 
        app.Run(new MyApp()) |> ignore 
#endif

Enjoy!  It’s yours to go run with.  I’d be interested to see people do fun enhancements to this code.  I’d also be interested to see people author a similar app on other platforms (iPhone, Android, HTML5 Canvas/Javascript, …) – I think perhaps the .NET platform is terrific in terms of languages and tooling to write this kind of stuff, but I have no comparative experience of my own, and I am curious to know how easy or hard it would be to mimic this app on another platform.

Posted in Uncategorized | 4 Comments »

Fun with turtle-graphics in F#

Posted by Brian on April 16, 2010

Once you have the mini LOGO implementation from my previous blog entry/screencast, it’s easy to start creating pretty fractals, like the Koch Snowflake:

KochSnowflake

The idea is simple; we start with a basic shape, such as an equilateral triangle:

let koch1 = [ Fwd 243; Right 120; Fwd 243; Right 120; Fwd 243 ]

and then we replace each “Fwd” segment with a more complex bit.  Here’s the Koch translation in F#:

let kochChange(drawing) = seq {
    for cmd in drawing do
        match cmd with
        | Fwd n -> 
            let x = n/3
            yield Fwd x
            yield Left 60
            yield Fwd x
            yield Right 120
            yield Fwd x
            yield Left 60
            yield Fwd x
        | c -> yield c }

That is, each time we encounter a “Fwd” segment, we replace it with LOGO code that does a little triangular sidestep around the middle third of the segment.  Every other command “c” is just kept as-is.

Then we can repeatedly apply this function to get more and more detail.  The picture above was created with:

let DRAWING = koch1 |> kochChange |> kochChange |> kochChange |> Seq.append [PenUp; Left 145; Fwd 120; Right 145; PenDown]

that is, three iterations of “kochChange” applied to the initial triangle (where the bit after “Seq.append” is just to move the turtle to a better initial starting position, so the drawing will all fit within the bounds of my canvas).

Pretty fun for 15 lines of code!

Here’s another pretty one:

DragonFractal 

The code for it is:

let rec Dragon1 size level = seq {
    if level > 0 then
        yield! Dragon (size*707/1000) (level-1)
        yield Left 90
        yield! Dragon1 (size*707/1000) (level-1) 
    else
        yield Fwd size
    }
and Dragon size level = seq {
    if level > 0 then
        yield! Dragon (size*707/1000) (level-1)
        yield Right 90
        yield! Dragon1 (size*707/1000) (level-1)
    else
        yield Fwd size 
    }

let DRAWING = Dragon 800 14

and it is fascinating to watch it develop in real time on the screen.

Pretty fun for 17 lines of code!

Just imagine what you can do if you add color!

One reason I love F# is that it is just so easy to do all this stuff.  I coded the LOGO implementation (less than 100 lines) on a whim for the backdrop of my prior screencast.  And I wrote this blog entry on fractals for an hour early this morning because I was having trouble sleeping.  Perhaps the hypnotic dancing turtle will lull me back to sleep…

Posted in Uncategorized | 1 Comment »

Using VS2010 to edit F# source code (and a little LOGO EDSL)

Posted by Brian on April 15, 2010

I made another F# screencast.  This one covers features of the Visual Studio editor; the backdrop is some code for doing LOGO (turtle graphics).  The screencast is available here (a couple clicks to download; I’m still working on some new file hosting to try to embed video).  It’s an 11-minute video that covers these topics:

  • Syntax highlighting (colors/fonts/sizes)
  • Intellisense (auto-complete, parameter help)
  • Squiggles (errors, warnings, navigation)
  • Type inference (tooltips)
  • Go To Definition, Navigation, and Search
  • Comment/uncomment blocks of code

If you’re new to Visual Studio or F#, I hope this is a great way to learn a little more about the VS IDE.  If you’re more experienced, you might still learn a new keyboard shortcut, or discover a previously-unknown feature of VS.

The code on the screen during the video is a little EDSL (Embedded Domain-Specific Language) for a subset of LOGO.  The full code is below; you can just copy it into an F# application project to run it.  It uses WPF for graphics, so you’ll need to add references to the standard WPF assemblies (PresentationCore, PresentationFramework, System.Xaml, System.Xml, UIAutomationTypes, and WindowsBase).  Have fun!

open System
open System.Windows 
open System.Windows.Controls 
open System.Windows.Shapes 
open System.Windows.Media 

[<Measure>] 
type deg
[<Measure>] 
type rad = 1

type Command =
    | Fwd of int
    | Left of int
    | Right of int
    | PenUp
    | PenDown
    | Repeat of int * Command list

let Repeat n cmds = Repeat(n,cmds)  // for syntactic elegance of EDSL

let MillisecondsToSleepBetweenCommands = 30
let WIDTH = 400.0
let HEIGHT = 300.0
let PI = System.Math.PI 
let deg2rad (d:float<deg>) : float<rad> = d * PI / 180.0<deg>

type MyWindow(theDrawing) as this =
    inherit Window(Title="Fun LOGO drawing")
    
    let canvas = new Canvas(Width=WIDTH, Height=HEIGHT)
    let mutable curX = WIDTH / 2.0
    let mutable curY = HEIGHT / 2.0
    let mutable curT = 0.0<deg>
    let mutable isPenDown = true
    let turtle = 
        // a right-pointing triangle centered about the origin
        let points = PointCollection [  Point( -5.0, -10.0)
                                        Point(  5.0,   0.0)
                                        Point( -5.0,  10.0)
                                        Point( -5.0, -10.0)  ]
        points.Freeze()
        let poly = new Polygon(Points = points, Stroke = Brushes.Green)
        canvas.Children.Add poly |> ignore
        poly
    let update() = async {
        Canvas.SetTop(turtle, curY)
        Canvas.SetLeft(turtle, curX)
        turtle.RenderTransform <- new RotateTransform(Angle=float curT)
        do! Async.Sleep MillisecondsToSleepBetweenCommands
        }
    do
        Canvas.SetTop(turtle, curY)
        Canvas.SetTop(turtle, curX)
        this.Content <- canvas
        this.SizeToContent <- SizeToContent.WidthAndHeight 
        this.Loaded.Add (fun _ ->
            async {
                do! Async.Sleep 200
                for cmd in theDrawing do
                    do! this.Execute(cmd)
            } |> Async.StartImmediate 
        )

    /// <summary>
    /// Execute a single LOGO command and update the screen
    /// </summary>
    /// <param name="command">The Command to be executed</param>
    member this.Execute command = async {
        match command with
        | PenUp ->
            isPenDown <- false
        | PenDown ->
            isPenDown <- true
        | Fwd n -> 
            let t = deg2rad curT
            let newX = curX + float n * cos t
            let newY = curY + float n * sin t
            if isPenDown then 
                let line = new Line(X1=curX, X2=newX, Y1=curY, Y2=newY, Stroke = Brushes.Black)
                canvas.Children.Add line |> ignore
            curX <- newX
            curY <- newY
            do! update()
        | Left t ->
            curT <- curT - float t * 1.0<deg>
            do! update()
        | Right t ->
            curT <- curT + float t * 1.0<deg>
            do! update()
        | Repeat(n,cmds) ->
            for _ in 1..n do
                for cmd in cmds do
                    do! this.Execute cmd
        }

let EYE = Repeat 1 [ PenDown; Repeat 8 [ Fwd 20; Right 45 ]; PenUp ]

let DRAWING = [ // go to good start location
                PenUp
                Left 135
                Fwd 80
                Right 135
                // left eye
                EYE
                // move right
                Fwd 80
                // right eye
                EYE
                // go to mouth corner
                Fwd 60
                Right 90
                Fwd 90
                // smile
                PenDown
                Right 45
                Fwd 60
                Right 45
                Fwd 100
                Right 45
                Fwd 60
                // circle around face
                PenUp
                Left 135
                Fwd 80
                Left 90
                Fwd 30
                PenDown
                Repeat 8 [
                    Fwd 100
                    Left 45
                ]
              ]

[<STAThread>]
do
    let app = new Application()
    app.Run(new MyWindow(DRAWING)) |> ignore

Posted in Uncategorized | 2 Comments »

Slides and demo code from last week’s Alt.NET workshop

Posted by Brian on April 14, 2010

Last Friday, Chris Smith and I gave a workshop on F#.  I wanted to post the slides and little code demos we used, so click here if you want to grab them.  Chris and I each had our own slide deck, so to go in order you’ll have to switch back and forth between the two decks, like we did during the talk (Chris’s intro, my part on functional programming, Chris’s section on OO programming, my part on Async, Chris’s case study).  At the end of my deck there’s also some bonus slides on units of measure that we didn’t have time for. 

The Visual Studio solution in the .zip contains the little code demo examples we did along the way.  Except for the one very cool sample near the end; I’m saving that for a separate blog post.  :)

Thanks to all who attended our workshop!

With the launch of VS2010 this week as well as the corresponding F# CTP (for those using VS2008), you’ve probably seen a lot of F# news of late.  Here’s one more item you may have missed, a nice article on the MSR site about F#, that speaks to the spirit of F# and the team.  “Fun is our keyword!” as I like to say.  :)

(More soon; I’m preparing a more in-depth screencast, and have more cool demos/samples in store…)

Posted in Uncategorized | 2 Comments »

Visual Studio 2010 key-binding quick reference cards are finally online! … oh yeah, and we shipped the product, too :)

Posted by Brian on April 12, 2010

As promised in the title, the keybindings are online.  Did you know that in the F# profile, Alt-apostrophe sends the current line to F# Interactive?  You would know if you had a copy of the keybinding quick-reference card sitting next to your desk!  (I should come up with a contest where the winners get printed copies of these cards, or stickers brandishing the new logo, as I’ve a cache of such goodies on my desk at work.)

Unless you have been hiding under a rock, you probably know that VS2010 has shipped, as well as the F# CTP update.  Huzzah!

If you’re new to the language and looking for links to get started, then this is a good blog post to have a look at.

Want a video?  I’m trying my hand at screencasts.  Here’s my first attempt.  (I used Expression Encoder 3, which made this pretty easy to record.)  Feedback welcome!

Posted in Uncategorized | 4 Comments »

Nine reasons to use F#

Posted by Brian on April 1, 2010

(Opening aside: Robin Milner, the father of ML, recently died, and Don’s blog briefly traces the influence of Milner’s work on both .NET generics and F# type inference.  As a programming-languages enthusiast, I enjoy learning about the histories of languages and seeing how feature cross-pollination has occurred across languages over time.)

Now that we’ve finished the VS2010 product, I’ve been spending less time writing code and more time giving talks; as mentioned in previous blog entries, I recently presented at TechReady and I will be doing an F# workshop along with Chris Smith for Alt.NET soon.  Preparing for talks gives me a chance to step back and think about how F# fits into the big picture, and how various audiences perceive F#.  There’s a wide variety of reasons that folks may find F# useful, but most talks/presentations just focus on one or two of these dimensions.  I wanted to organize my thoughts on the wider picture, hence this blog post.

The following “Nine reasons to use F#” are presented in no particular order.  The list is not necessarily distinct (some elements overlap) or complete (this is just a list I came up with today), and not every reason in the list will resonate with every reader.  But they are all ‘real’ reasons, in the sense that they’re representative of individuals or teams out in the world who are already using F# today.  I think it’s interesting how much diversity there is in the list.

Without further ado, here are 9 reasons to use F#:

  1. Units of measure.  If you’re working in domains that use numbers to represent physical quantities like kilograms, meters, and seconds (or pixels versus inches, or dollars versus euros, or …) you can use the F# type system to ensure that the dimensional analysis works out.  Nearly everyone who uses this feature has a “wow” experience where the compiler finds an unexpected bug (it happened for me when working on the 2008 ICFP programming contest).  In certain domains, this kind of static typechecking is a really killer feature. You can learn more about F# units here, here, or here.
  2. Functional programming with .NET.  If you need to author a component that’s especially amenable to functional programming techniques (like game AIs, statistical modeling, symbolic computing, …), and you need to interoperate with SQL, Excel, or XBox, or run in the browser with Silverlight, or run in the cloud with Azure, or do WCF, WPF, or WinForms, or easily talk to your existing C#/VB/C++/COM code, then F# is where it’s at.  The .NET platform has great reach and integration with a variety of technologies, and F# extends the platform by providing a way to do first-class functional programming and get seamless integration with all these technologies.
  3. Explorative programming with the REPL.  The F# Interactive tool window makes it easy to execute small bits of code on the fly.  When working with large data sets or mathematical models, this makes it easy to explore the data, play “what-if” games, and directly interact with the data/models in a very “live” way.  When coupled with libraries for data visualization, this enables some really cool stuff.
  4. Asynchronous and parallel programming.  I’ve blogged recently about F#’s unique programming model for writing non-blocking code for asynchronous I/O.  The async programming model also extends to parallelizing CPU intensive work, and F#’s library also makes is straightforward to react to events, use lightweight agents, or run code on the GPU.  (There’s actually tons of content on the web on these topics; in the previous sentence, I just sprinkled in some links to Don’s blog.)  Multi-core is here, and F#’s language constructs and libraries are very well-suited to parallel programming (not to mention some intrinsic advantages of the functional programming style).
  5. Embedded domain-specific languages (EDSLs).  A variety of F# features (including ability to define new operators, currying and function application syntax, quotations, type inference and overall lightweight syntax) make F# a good language for creating EDSLs.  Some examples include FsUnit for unit testing, FAKE for authoring build scripts, FParsec for parsing, and WebSharper for web applications.
  6. Scripting.  Every so often, I need to write a script, but I use perl and batch files infrequently enough that I’m always forgetting the syntax of those.  Nowadays I use F# for a number of scripting tasks, both in the sense of “FSI is the new perl” and for tiny tasks (e.g. if I can’t recall the ascii value of character ‘A’, I just type “int ‘A’;;” into the F# Interactive window).  I know at least a couple folks using .fsx files where previously they would have used batch files or perl scripts.  For me, F# is the first “software engineering” language I know that’s also a good “scripting language”.
  7. A practical language for academia.  Back when I was a college undergrad, the first real programming language I learned in school was Pascal.  (Yes, this was back before C#, or even Java; I’m an old man.)  In university, it seems there’s been a tension between teaching more ‘pure’ and ‘CS-y’ languages and teaching more pragmatic languages (e.g. that you can likely use for gainful employment when you get out of school).  This is still a topic of much debate, but I think we’re in a much better state now, with languages including Java, C#, Scala, Python, and F#, then we were a decade or two ago; there are now a variety of languages that are decent both as “good introductory CS languages” and “useful real-world languages”.
  8. Expand your mind and career.  I think there are a fair number of folks that use C# or VB in their day job, who would like to learn more about functional programming, but find that diving head-first into Haskell is too difficult or time-consuming.  F# provides an avenue to go more gently into the waters of functional programming, leveraging one’s existing .NET and Visual Studio familiarity.  A lot of folks who are just trying F# as a hobby have reported that they’ve learned new strategies/algorithms that they can apply back in their day job (e.g. using C#), hurray.
  9. Fun!  I’m constantly hearing from people how much they enjoy using F#.  Who doesn’t love being able to get a job done and have fun doing it?

In a given blog post, or talk, or video on F#, it’s easy (and indeed often appropriate) to “pigeonhole” the language, focusing on one particular useful aspect.  But on occasion it’s useful to step back and see the myriad ways that people perceive and use F#.  So this is one attempt at that, a scattered list that suggests various reasons why people are using F# across the roles of hobbyist, academic, and real-world developer.  It’s been great giving this language legs and now seeing all the ways people are applying it.

As an aside, one way you know that your Microsoft product is “real” is when they give you a T-shirt with your product logo on it.  :)  Here’s a photo of the back of a shirt we (the VS managed languages folks) just received at work:

000_0142

Good ol’ SWAG…

As a final aside, a revised language spec for the RC release is finally online.  Thanks to all those who have reported bugs; we continue to refine this document.

Posted in Uncategorized | 3 Comments »