Inside F#

Brian's thoughts on F# and .NET

On Sequences, SyndicationFeeds, and Syntax…

Posted by Brian on March 30, 2008

Back when I worked on WCF, one of the cool features we delivered in .NET 3.5 was support for consuming and publishing SyndicationFeeds (RSS or Atom).  So that provides a nice domain for a short bit of code that will let me talk about some pieces of F#.(Incidentally, it was when writing this code that I noticed my Windows Live Space blog did not yet have a title, so I corrected that today.  Like the new title?)  Anyway, here’s the code:

open System.Xml
open System.ServiceModel.Syndication

let reader = XmlReader.Create("http://lorgonblog.spaces.live.com/feed.rss")
let feed = SyndicationFeed.Load<SyndicationFeed>(reader)
printfn "Feed Title: %s" feed.Title.Text
printfn "Feed Description: %s" feed.Description.Text
feed.Items |> Seq.iter (fun item ->
    printfn "entry: %s %s" (item.PublishDate.ToString("dd MMM yyyy")) item.Title.Text
)

As you can guess, this prints out a summary of my blog, by reading the RSS feed and then printing out some select bits.  The part of F# I want to highlight here is "Seq".  The Seq module contains methods for dealing with sequences of things.  (I haven’t discussed modules yet on this blog, but for the curious reader in the meantime, it would not hurt too much to equate "module" with "static class" in your head.)  In F#, "seq" is just a short alias for "IEnumerable".  Thus in the code below, x, y, and z each describe objects with the same type and value:

let x = [1; 2; 3] :> int seq 
let y = [1; 2; 3] :> seq<int
let z = [1; 2; 3] :> System.Collections.Generic.IEnumerable<int>

The ":>" operator does an "upcast"; in the examples above I create list<int>s, but then upcast them to IEnumerable<int>s so that the variables are assigned those types.  Each variables has the same type, just spelled three different ways.  "IEnumerable<int>" is just what you already know from C# programming.  "seq<int>" is the same thing, only using "seq" as an alias for "IEnumerable".  And "int seq" is another way to write "seq<int>"; this is just one of those OCaml-isms, where "’a GenericType" means the same thing as "GenericType<‘a>".  You see this most often with lists – e.g. people may write code that mentions the type "int list", which is just another way to write "list<int>".  (For sequences of ints, I expect I shall prefer the spelling "seq<int>", though this is one of many areas of F# style/preference where I think we’ve yet to decide on a coding guideline.)

Anyway, sequences are commonly used in F#, and just as in C#, one of the most common things to do with them is to perform some operation on each element of the sequence.  This is what "Seq.iter" does in the original code example.  This F# code:

feed.Items |> Seq.iter (fun item -> 
    printfn "entry: %s %s" (item.PublishDate.ToString("dd MMM yyyy")) item.Title.Text 
)

does the same as this C# code:

foreach (var item in feed.Items)
{
    Console.WriteLine("entry: {0} {1}", item.PublishDate.ToString("dd MMM yyyy"), item.Title.Text);
}

As is typical in F#, we start with the sequence itself ("feed.Items" in this example) and then use the pipelining operator "|>" to send that data through some function.  "x |> f" just evaluates to "f x", which means that we could write

Seq.iter (fun (item : SyndicationItem) ->
    printfn "entry: %s %s" (item.PublishDate.ToString("dd MMM yyyy")) item.Title.Text
) feed.Items 

instead.  However when we write it that way, we have to explicitly provide a type annotation for "item" to say that it is a "SyndicationItem".  This is because type-inference in F# works in a left-to-right manner: when written without the pipeline operator, we reach "item.PublishDate" without yet knowing the type of "item" and so we are out of luck, whereas when we do use the pipeline operator, "feed.Items" (which is a seq<SyndicationItem>) occurs first, so that by the time we reach "fun item" we can infer the proper type (SyndicationItem) based on the information we’ve already seen.  Writing code using the pipeline operator is one of those things that feels a little weird at first but then quickly becomes second nature.  (I think I’ll have to have to devote a future blog post to pipelining.)

This was going to be where I stopped writing, but as I was re-reading this blog entry, I found myself wondering, if "Foo<T>" and "T Foo" are two ways to spell the same type name, is there a way to do it if there is more than one generic type argument?  And there is:

type Pair<‘a,’b>(a : ‘a, b : ‘b) =
    member this.A = a
    member this.B = b

let p1 = new Pair<int,string>(42, "life")
let p2 : (int, string) Pair = p1

In that example, p1 and p2 have the same type, spelled different ways.  That is, "Pair<int,string>" can also be written "(int,string)Pair".  This is even true in the class definition, that is, I can write

type (‘a, ‘b) Pair2(a : ‘a, b : ‘b) =
    member this.A = a
    member this.B = b

But I have never seen anyone write F# code that way unless they were originally an OCaml programmer.  I think it is pretty safe to assume that the vast majority of F# code you will encounter will use the "Angle<Brackets>" style for writing generic types.  I’ll try to be careful and consistently use the angle brackets style in my code in the future.

About these ads

One Response to “On Sequences, SyndicationFeeds, and Syntax…”

  1. Stephen said

    Thanks! The first section of code worked for me for reading a commercial atom 1.0 feed. I changed it to read the second URL from the Links in each Item, ie. feed.Items |> Seq.map (fun item -> item.Links.[1].Uri). The translation to (C# foreach (SyndicationItem item in feed.Items…) also worked without a glitch. Only four lines of code, but saved me hours from working it our from first principles. This is my first look at WCF, and having done the same feed in PHP, I must say that WCF rocks!!!

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

 
Follow

Get every new post delivered to your Inbox.

Join 30 other followers

%d bloggers like this: