Thursday, August 25, 2011

Adding NuGet Support to F# Interactive

A little over a week ago, Rick Minerich and I had a brief conversation about how cool it would be to install NuGet packages via the F# Interactive (FSI). After some research, there seems to be 3 options for accomplishing this:

1. Write a script that is loaded when the FSI starts.
2. Modify the source of FSI.exe (which can be found at http://fsharppowerpack.codeplex.com/).
3. Create an alternate F# Interactive Window VSIX that starts the FSI.exe as a new process as well as provides new commands.

In this post, I'll explore the first of these options.

Building the Script:

There are 3 steps to accomplishing the desired goal with option 1.

1. Create a F# script that has the ability to retrieve packages from NuGet and interact with Visual Studio. The script that I used to accomplish this can be found here.

Note: NuGet must be installed on the machine. Additionally, you will need to change the path to the NuGet.Core.dll reference if running a 32-bit version of Windows. This will be evident if you receive an error like the following when starting the F# Interactive Window - "error FS0084: Assembly reference 'C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\NuGet.Core.dll' was not found or is invalid".

2. Update the FSI command line options in Visual Studio by going to Tools | Options | F# Tools | F# Interactive. Add "--use:<Path>\FsiExtension.fsx" to the "F# Interactive Options" value (see example below). Note: A full list of available FSI options can be found at http://msdn.microsoft.com/en-us/library/dd233172.aspx.


3. Open the F# Interactive Window (or reset the session if the window is already opened).

Taking it for a Test Drive:

Now that you are setup, it's time to try out the new functionality.

1. Create a new F# project and add a F# Script File (if one doesn't currently exist).

2.  Add some code (such as the following example from the Getting Started with F# PowerPack - Part 2 blog post) to the F# Script File:
open Microsoft.FSharp.Control  
  
let rec job = async {     
        for i in 1 .. 20 do    
            printfn "doing some work"  
            do! Async.Sleep 300  
            worker.ReportProgress i  
    }  
    and worker : AsyncWorker<_> = AsyncWorker(job)  
  
worker.ProgressChanged.Add(fun jobNumber -> printfn "job %d completed" jobNumber)   
worker.Error.Add(fun err -> printfn "Error: %A" err.Message)  
worker.Completed.Add(fun _ -> printfn "All jobs have completed")  
worker.Canceled.Add(fun _ -> printfn "Jobs have been canceled")  
  
worker.RunAsync() |> ignore  

3. In the F# Interactive Window, type InstallPackage "FSPowerPack.Core.Community";; and execute it. This will fire off the process to retrieve the FSPowerPack.Core.Community NuGet package from NuGet Gallery. The F# Interactive Window should look like the following once the process is complete:


This does two things:

1. It retrieves the specified NuGet package.
2. It adds any appropriate references to the fsx file. Your code will now look something like this:
#r @"C:\git\TestFsDte\packages\FSPowerPack.Core.Community.2.0.0.0\Lib\Net40\FSharp.PowerPack.dll"
open Microsoft.FSharp.Control  
  
let rec job = async {     
        for i in 1 .. 20 do    
            printfn "doing some work"  
            do! Async.Sleep 300  
            worker.ReportProgress i  
    }  
    and worker : AsyncWorker<_> = AsyncWorker(job)  
  
worker.ProgressChanged.Add(fun jobNumber -> printfn "job %d completed" jobNumber)   
worker.Error.Add(fun err -> printfn "Error: %A" err.Message)  
worker.Completed.Add(fun _ -> printfn "All jobs have completed")  
worker.Canceled.Add(fun _ -> printfn "Jobs have been canceled")  
  
worker.RunAsync() |> ignore  
4. You can also see what packages are installed by typing ShowInstalledPackages();;

Conclusion:

While this is hardly a perfect solution, hopefully it will at least spawn a few ideas. This implementation has several known issues (and probably several that are unknown). Let me know if you have ideas on better approaches or improvements.

No comments:

Post a Comment