Inching Forward

Getting into FunScript

Nov 10, 2015

This post explores what it takes to set up an environment for writing a simple FunScript application from scratch using a non-Visual Studio environment. It’s targetted at the beginner, using a step-by-step process that tries to remove a few stumbling blocks I encountered when trying to do the FunScript tutorial without using an example solution. The dev environment is Xamarin Studio (version 5.9.8) running on a Mac. If you’d like to see other environments, let me know in the comments.

Create a new F# solution

With Xamarin Studio running, create a new Solution by clicking File > New > Solution. In the New Project window under Other, select .NET. In the middle panel, choose Console Project, then click the oval to the right and choose F#:

The New Project dialog

Click the Next button to advance to the “Configure your new project” window. For this project, I chose the name “HelloFunScript”:

Configure your new project window

Click the Create button and Xamarin Studio should create your project, then load the auto-generated Program.fs file. All of our code will go in this file.

Adding FunScript

In order to use FunScript, we have to add a couple of packages to the project. From the Project menu, click the Add NuGet Packages… option. With the “Add Packages” window displayed, in the search box in the upper-right corner type “funscript lib”. This will filter the package list down enough to make the 2 packages we need easier to find: FunScript and FunScript.TypeScript.Binding.Lib. Check both of these packages and click the Add Packages button:

The Add Packages window

The FunScript package contains the FunScript compiler which takes F# code and turns it into JavaScript. The FunScript.TypeScript.Binding.Lib allows us access to the DOM, BOM, and other JavaScript libraries (more on this in a future post).

Our first application

Replace the contents of Program.fs with the following:

[<FunScript.JS>]
module Program

open FunScript.Compiler
open FunScript.TypeScript
open System.IO

let main () = 
    Globals.window.alert("Hello, world!")

do
    let js = Compiler.Compile(<@ main() @>, noReturn = true)
    let html = sprintf "<html><body><script>%s</script></body>" js
    File.WriteAllText("index.html", html)
    System.Diagnostics.Process.Start("index.html") |> ignore

Let’s go over what all this means…

Program.fs |> grok

 [<FunScript.JS>]
 module Program

Here we create a module named Program that our application code belongs to. The module is marked with the FunScript.JS attribute. This attribute uses some fancy metaprogramming that allows the FunScript compiler to translate our F# code into JavaScript code. Note that FunScript.js is an alias for ReflectedDefinition: you will see both used on the FunScript site and and in the examples.

open FunScript.Compiler
open FunScript.TypeScript
open System.IO

The above import declarations give us access to the FunScript compiler, the JavaScript global environment via a TypeScript layer, and IO which we need to access the File object.

let main () = 
    Globals.window.alert("Hello, world!")

The main function acts as the entry point to our application. The FunScript.TypeScript.Globals namespace gives us access to the JavaScript BOM window.alert function. To see the huge list of JavaScript globals available, you can use code completion against the Globals type.

do
    let js = Compiler.Compile(<@ main() @>, noReturn = true)
    let html = sprintf "<html><body><script>%s</script></body></html>" js
    File.WriteAllText("index.html", html)
    System.Diagnostics.Process.Start("index.html") |> ignore

In a do binding, we invoke the FunScript compiler with our quotation, which will return a JavaScript string we bind to js. The sprintf function is used to embed js into a crude html string, which we then write out to a file named “index.html”. The last line attempts to open the html file using [Process.Start()](https://msdn.microsoft.com/en-us/library/53ezey2s(v=vs.110).aspx). The result, a Process, is piped to ignore since the do binding expects a return type of unit. For such a simple example like this, just opening the file will work. The FunScript examples use a more sophisticated launcher (invoked by do Runtime.Run()) which starts a web server to host the generated javascript.

Click the run button (or choose Run > Start Without Debugging). If everything works, the index.html file will get written and the default browser will launch and open it. You should be presented with the alert:

Hello World alert window

Sweet–the first step to FunScript victory! If you dismiss the alert, then examine the source of the page, you can see what the FunScript compiler generated:

<html><body><script>var Program__main$;
Program__main$ = (function(unitVar0)
{
    return ((window.window).alert("Hello, world!"));
});
Program__main$()</script></body></html>

Hopefully this post showed how easy it is to get started with FunScript. Go forth to the FunScript site, then check out and play around with the examples. Have fun!

UPDATE 09/28/2016: Check out Fable, a newer effort that compiles F# to JS.


#funscript #fsharp