Inside F#

Brian's thoughts on F# and .NET

“FSI is the new perl”

Posted by Brian on May 9, 2008

When I need a quick-and-dirty fifteen-line script for a one-off task involving text files, I often use perl.  The problem is, I use perl infrequently enough that each time I decide to use it, I usually need to give myself a five-minute refresher on the syntax and common functions before I can get the job done.

On the other hand, I use F# every day.

Thus today’s blog entry is just to call out how easy it is to write little handy scripts with F#. 

FSI

I haven’t talked about FSI much.  You can use F# Interactive from the command-line (fsi.exe), or as a Visual Studio Add-in (inside VS, select "Tools\Add-in Manager…" and click the check box next to F# interactive).  I like the VS add-in because it makes it easy to use the VS text editor as a "scratch pad" with syntax highlighting and intellisense.  When you have code you want to execute, just select it and press Alt-Enter and the code will be sent to the FSI window and executed.

A sample script

Suppose I want to know what percentage of the sample .fs files in the F# distribution use the lightweight syntax option (#light).  (EDIT, Jan 2010: #light is now the default, so this script is no longer useful for this particular task, though it does show how to find what percentage of files contain an arbitrary substring.)  I can quickly hack together:

    open System.IO

    let Average l =
        List.reduce (+) l / List.length l
    
    let rec AllFiles dir = 
        seq { yield! Directory.GetFiles(dir)
              for subdir in Directory.GetDirectories(dir) do yield! AllFiles subdir }

    [ for file in AllFiles @"C:\Program Files\FSharp-1.9.4.15\samples" do
       if file.EndsWith(".fs") then
           if File.ReadAllText(file).Contains("#light") then 
               yield 100
           else 
               yield 0 ]
    |> Average           
    |> printfn "%d%% of files use #light"

Highlight this code, press alt-enter, and FSI says

    val Average : int list -> int
    val AllFiles : string -> seq<string>
    91% of files use #light

Cool.  I wonder which files don’t use #light?  I can just add a little code to the "else" branch:

           else 
               do printfn "no #light: %s" file
               yield 0 ]

and then highlight the last block of code (from "[for…" to the end of the file) and hit alt-enter again, and I see

    no #light: C:\Program Files\FSharp-1.9.4.15\samples\fsharp\Differentiate\lex.fs
    no #light: C:\Program Files\FSharp-1.9.4.15\samples\fsharp\Differentiate\pars.fs
    no #light: C:\Program Files\FSharp-1.9.4.15\
samples\fsharp\FLinq\dumper.fs
    no #light: C:\Program Files\FSharp-1.9.4.15\samples\fsharp\FLinq\program.fs
    no #light: C:\Program Files\FSharp-1.9.4.15\samples\fsharp\Parsing\ast.fs
    91% of files use #light
    val it : unit = ()

in the FSI window.  Neat.

There might be shorter or more elegant ways to accomplish the same goal with F# (or perl, or what have you), but my focus today is on writability, demonstrating that it’s easy to hack little scripts like this in less than five minutes.

Some F# details

The script above demos some F# bits that I don’t think I’ve talked about before.  The "seq { … }" code is an example of a sequence comprehension, another example of F#’s computation expression syntax.  Recall that "seq" (short for "sequence") just means "IEnumerable".  Inside a sequence comprehension, "yield" means the same thing as it does in a C# iterator block, and "yield!" means nearly the same thing, except that you pass it a sequence rather than a single item.  (In other words, "yield! s" is like "for i in s do yield i", where s is a seq<‘a> and i is an ‘a.)  So AllFiles returns a seq<string> that recursively enumerates all the file names under some directory.

The portion in square brackets is a list comprehension, which is pretty much just like a sequence comprehension, except the result is a list<‘a> rather than a seq<‘a>.  Thus we end up with a list of integers, with the value 100 for each file containing "#light", and 0 for the other files.  We average the values in the list, and we get the percentage of files that use #light.

That’s all for today – just some quickie fun I wanted to share!

Advertisements

One Response to ““FSI is the new perl””

  1. Unknown said

    Brian,Nice blog. I wanted to convince my manager that F# was the new Perl too. So it helps to see this.By the way you say there might be shorter or more elegant ways to write this, "but" you wanted to focus on writability to show how easy it is. But the two are not mutually exclusive. If you showed shorter more elegant ways than you could make an even better case that F# was easier or just as easy as Perl. Using yield and seq seems unnecessarily complex. I think you could have done this:let ave list =(float) ( Array.sum list) / (float)(Array.length list)let files = Directory.GetFiles("c:/test") |> Array.filter (fun file -> file.EndsWith(".txt")) |> Array.map (fun file -> if File.ReadAllText(file).Contains("dog") then 100 else 0) |> ave

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: