共用方式為


F# Scaling from Explorative to .NET Component - F# Talk @ TechEd 2010

Last Thursday I gave an F# talk at TechEd New Orleans titled "F# in Visual Studio 2010".  The talk aimed to highlight a few ideas:

  • F# enables exciting new developer scenarios in Visual Studio (explorative bottom-up programming in the F# Interactive)
  • F# can leverage many/most of the powerful developer tools in Visual Studio (projects, debugging, editing, help integration, etc.) and .NET (produce and consume vanilla .NET APIs)
  • F# has unique strengths in key target domains and extends the .NET platform to these audiences
  • F# scales smoothly from explorative to .NET component development (start with ideas and iteratively and smoothly add structure as methods, types and projects)

The video of the talk is available now on TechEd Online and here is the demo code below for those interested in taking a look. 

File iconTechEd2010Demos.zip

Those who read my blog may recognize a number of the examples as things I've posted about in the past.   The F# Silverlight apps used in the demos are also available in those previous posts.

There was one theme that seemed to resonate especially well - here's a few more thoughts on the idea of scaling from explorative to component building in F# from the perspective of using the "tab" refactoring in F#.

"tab" as Extract Method (or, more generally, Encapsulate)

Indentation is meaningful in F#, and is used to indicate nesting.  This, along with type inference and a strong uniformity of syntax at different levels of nesting, enables a simple but really fundamental shift to how you can think about encapsulation in F#. 

An example. I start by solving a problem by writing code directly at the top level.  I don't know what shape the solution has to take yet, but I leverage the F# Interactive to prototype and explore the solution.

 

 let url = "https://www.yahoo.com"
let req    = WebRequest.Create(Uri(url))
let stream = req.GetResponse().GetResponseStream()
let reader = new StreamReader(stream)
let html   = reader.ReadToEnd()

html.Length

Notice that because we're in the F# Interactive, we tend to have some sample data we want to experiment with bound in our environment - like "url" above.

Once we establish that this code does what we want - we can "give it a name" - using a simple and local set of changes:

 let url = "https://www.yahoo.com"

let http url = 
    let req    = WebRequest.Create(Uri(url))
    let stream = req.GetResponse().GetResponseStream()
    let reader = new StreamReader(stream)
    let html   = reader.ReadToEnd()
    html

let html = http url
html.Length

We just hit "tab", and then wrote out the signature for the new function.  Note that we were able to "parameterize over" any of the names used in this piece of code that we wanted to be variable in our abstraction.  So in this case, we parameterize over "url".  Any later dependencies on the results of the original code can be left intact by re-binding the name "html" to a call to the new function - thereby making this a truly local code change.

Notice that a key to this was the uniformity of "let" between the top level and the function body scope. 

Later in my coding I may end up with something like this:

 let username = "abc"
let password = "def"
    
let http url = 
    let credentials = NetworkCredential(username, password)
    let req    = WebRequest.Create(Uri(url), Credentials=credentials)
    let stream = req.GetResponse().GetResponseStream()
    let reader = new StreamReader(stream)
    let html   = reader.ReadToEnd()
    html
    
let homeTimeline() = http "https://api.twitter.com/statuses/home_timeline.xml"
let publicTimeline() = http "https://api.twitter.com/statuses/public_timeline.xml"
let userTimeline() = http "https://api.twitter.com/statuses/user_timeline.xml"

At this point, I have some interesting functionality - and may want to introduce a true component to encapsulate the implementation and expose an intentionally designed API.  OO is an excellent and proven tool for component design, and is a key part of F# programming.  It is also very easy to smoothly transition from simple top level coding into F# component design with OO.  And again - it's mostly just about "tab".

 let username = "abc"
let password = "def"

type Twitter(username, password) =     
    let http url = 
        let credentials = NetworkCredential(username, password)
        let req    = WebRequest.Create(Uri(url), Credentials=credentials)
        let stream = req.GetResponse().GetResponseStream()
        let reader = new StreamReader(stream)
        let html   = reader.ReadToEnd()
        html
    
    let homeTimeline() = http "https://api.twitter.com/statuses/home_timeline.xml"
    let userTimeline() = http "https://api.twitter.com/statuses/user_timeline.xml"
    let publicTimeline() = http "https://api.twitter.com/statuses/public_timeline.xml"
    
    member this.HomeTimeline() = homeTimeline()
    member this.UserTimeline() = userTimeline()
    member this.PublicTimeline() = publicTimeline()

let twitter = new Twitter(username, password)
let homeTimeline = twitter.HomeTimeline()
let userTimeline = twitter.UserTimeline()
let publicTimeline = twitter.PublicTimeline()

Notice how similar this transition was to the change we needed to give the "http" function a name.  In this case, we just tab in all the code except the things we want to parameterize over, give the signature of the type and it's constructor (again, the things we want to parameterize over), and then define the API we want to expose from the functionality as "member" definitions.

So we've now introduced a true .NET class with the API we want.  The last step is to transition from the explorative environment to a component.  We can just take the code above - strip off the sample data that was just there to experiment in the F# Interactive - and compile into a .NET assembly:

 namespace TwitterAPI

open System
open System.Net
open System.IO

type Twitter(username : string, password : string) =     
    let http url = 
        let credentials = NetworkCredential(username, password)
        let req    = WebRequest.Create(Uri(url), Credentials=credentials)
        let stream = req.GetResponse().GetResponseStream()
        let reader = new StreamReader(stream)
        let html   = reader.ReadToEnd()
        html
    
    let homeTimeline() = http "https://api.twitter.com/statuses/home_timeline.xml"
    let userTimeline() = http "https://api.twitter.com/statuses/user_timeline.xml"
    let publicTimeline() = http "https://api.twitter.com/statuses/public_timeline.xml"
    
    member this.HomeTimeline() = homeTimeline()
    member this.UserTimeline() = userTimeline()
    member this.PublicTimeline() = publicTimeline()
 

image

And then we can use this from C# (or VB, or any other .NET language):

 using System;
using TwitterAPI;

class Program
{
    static void Main(string[] args)
    {
        Twitter twitter = new Twitter("abc", "def");
        string homeTimeline = twitter.HomeTimeline();
        Console.WriteLine(homeTimeline);
    }
}

Summary

The TechEd talk covered a number of interesting aspects of using F# in Visual Studio - tools, core syntax, some examples of current commercial users, and the physical structure of F# projects used in client applications.  But the theme of scaling from explorative programming to .NET component is one that I find particularly powerful, and which is a really unique strength of the F# language.

 

File iconTechEd2010Demos.zip

Comments

  • Anonymous
    June 14, 2010
    The comment has been removed

  • Anonymous
    April 05, 2012
    Hi Luke, the presentation is excellent! Though I set my twitter username and password, I got "System.Net.WebException:(401) unauthorized" with homeTimeline() and userTimeline(), only the publicTimeline() is working fine. And my twitter account is working fine, too. What is the problem? How should I solve it or make a further test? Thanks.

  • Anonymous
    April 09, 2012
    The problem was solved with your later post "OAuth in F#" thanks but there is another problem that I can not convert the third project "3.WebSilverlightApps" successfully with VS2011 I was asked to "install the latest silverlight developer runtime" but actually it is already the latest.