Scripting in F#
The thing you hear most about F# is that it is multi-paradigm, meaning that you can use it to code in multiple styles of programming. But F# spans multiple-domains too. F# is not only well suited for quantitative computing, but it is surprisingly well suited for scripting as well.
The Desktop Scripting Scenario
The use-case for scripting is that you want to automate some task but don’t feel like resorting to writing a full-blown application to do it for you. For example, on the test team we use Visual Studio Team Test edition to gather Code Coverage for the F# product. In order to get our bits ready for code coverage I need to do the following:
- Un-GAC the assembly
- Instrument it using a filter
- Disable strong name verification
- GAC it
- Delete NGEN images
- NGEN it
- [Run tests]
- Un-GAC the instrumented binary
- Delete NGEN images
Those steps are tedious as-is, let alone when you want to repeat the process for 10 different binaries!
Now I could write some wizard-style application to manage the general task of instrumenting assemblies and setting up the machine, but now I need to worry about source control, versioning, and installing this new app. Pffft whatevs.
Instead to solve this problem I’ve written an F# Script File (.fsx), which is an entire F# Project rolled into one file.
Note: Before you get excited and remind me that Windows PowerShell exists, I’ll just say the only thing better than learning a new programming language AND a new shell language, is just learning a new programming language.
So what exactly is an F# script file?
An F# script file is a normal F# source code file, except that .fsx files have a few extra capabilities.
Windows Explorer Integration
Double-Clicking on a .fsx file opens it up in Visual Studio. This gives you intellisense for editing. And the ability to highlight some code and run in F# Interactive for Visual Studio.
Right-Clicking on it has the context menu in Explorer shows an ‘Run with F# Interactive’ action which will execute the script directly through FSI.
Extra perks of F# Scripts
F# script files also have extra features available to them to compensate for not having project support. Again, think of .FSX files as a single-file project.
Adding Reference Via Code
When editing an F# Project in Visual Studio, to reference a separate assembly you can add a project reference through the UI. In F# scripts however you can just type #r and load an assembly. By default it will pick things out of the current directory and/or search paths (see below). (This was always allowed in previous releases of F#, but in the CTP this is only available to .fsx script files.)
#r "System.Core.dll"
open System.Collections.Generic
let hs = new HashSet<int>()
Add Directories to the Search Path
#r Will get things out of the GAC, but fully qualifying reference paths is a pain. You can use #I however to add a ‘search path’ to indicate where to look for references.
#I @"C:\Program Files (x86)\Reference Assemblies\Microsoft\VSTO\v9.0"
#r "Microsoft.Office.Tools.Common.v9.0.dll"
#r "Microsoft.Office.Tools.Excel.v9.0.dll"
Load Other Source Files
When I said F# Script files were one-file projects, I slightly lied. By using #load you can load additional source files into the script. This way you can reuse library-type code. Note that this is not recommended as a way for breaking up large scripts. If your script is too large for one file, you should consider creating an actual F# project.
#load "HelperLibrary.fs"
let x = HelperLibrary.SomethingAwesome()
Script Snippets
For building your own F# scripts, here are some simple building blocks to start from:
/// Get all files under a given folder
open System.IO
let rec allFilesUnder baseFolder =
seq {
yield! Directory.GetFiles(baseFolder)
for subDir in Directory.GetDirectories(baseFolder) do
yield! allFilesUnder subDir
}
/// Active Pattern for determining file extension
let (|EndsWith|_|) extension (file : string) =
if file.EndsWith(extension)
then Some()
else None
/// Shell executing a program
open System.Diagnostics
let shellExecute program args =
let startInfo = new ProcessStartInfo()
startInfo.FileName <- program
startInfo.Arguments <- args
startInfo.UseShellExecute <- true
let proc = Process.Start(startInfo)
proc.WaitForExit()
()
Example F# Script
Putting the code above together yields a script for opening up each .fs or .fsi file under the script folder in Notepad. Not bad for six lines of code, and with some work you could even clean it up further taking advantage of Language Oriented Programming techniques…
// Launches all .fs and .fsi files under the current folder in Notepad
open System
allFilesUnder Environment.CurrentDirectory
|> Seq.filter (function
| EndsWith ".fs" _
| EndsWith ".fsi" _
-> true
| _ -> false)
|> Seq.iter (shellExecute "Notepad.exe")
I’ve found F# scripts to be useful in my work. I’d love to hear from you about what sorts of applications you use FSX files for.
Comments
Anonymous
September 12, 2008
PingBack from http://hoursfunnywallpaper.cn/?p=5958Anonymous
September 22, 2008
In a previous post I talked about how to take advantage of .FSX (F# Script) files to automate tasks forAnonymous
October 08, 2008
Is loading scripts into other scripts really deprecated? I'm porting the code in "Programming Collective Intelligence" to F# just now, and am finding the flexibility of keeping everything interpreted invaluable. At the same time there is a lot of mix and match functionality that I am not going to keep typing. (Bad enough that everyone in the world has to write a function to do HTTP GETs, unforgivable to have to write it in every script.) Actually, it's all working pretty well so far, but I'd love to know what other people consider good practice when developing interactively. I could compile everything into DLLs, but this fluidity is a good thing. I'd be loathe to give it up.