Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Object Oriented F# – Extension Everything

A post by Jeremy Miller caught my eye this morning in regards to extension methods in Javascript.  While I think that’s pretty interesting, I don’t think it’s a real fair comparison.  Instead, I want to revisit C# and even F# with regards to extension methods, because there are a few things I wanted to point out.  This is the start of a series covering object oriented programming techniques and how they are used in F#.  Note that F# is not only a functional language, but it is a general purpose programming language that supports functional, imperative and object oriented techniques.  I hope this series is useful for pointing out that F# fits the need very nicely for object oriented constructs, which is seldom covered.


Extension Methods in C# 3.0

With C# 3.0 came the introduction of extension methods which were introduced as part of certain technologies that were LINQ-enabling.  Using these, we could add methods, and only public methods, to any given class we choose.  So, this gave us the ability to do add methods such as ForIndex to an IEnumerable<T> class such as this:

static class Extensions 

    public static void ForIndex<T>(
      this IEnumerable<T> items, 
      Action<int, T> action) 
    { 
        var index = 0
        foreach(var item in items) 
        { 
            action(index, item); 
            index++; 
        } 
    } 

var range = Enumerable.Range(1, 10); 
range.ForIndex((i, item) => Console.WriteLine("{0} – {1}", i, item));

 

But overall, I thought this was nice, but didn’t go quite far enough.  Why not extension static methods, properties, events, etc?  I’m sure at one point those were considered but somehow dropped along the way, and I think it’s a shame quite frankly.    

Where should it be?  If this functionality were to exist in C#, how might it look?  Unfortunately, the way it was designed in C# 3.0, it makes it a tad hard to extend for static methods and properties.  For example, how might you declare a get or set if what you’re declaring is a method such as this?

public static bool IsEven(this int value)
{
    get { return value % 2 == 0; }
}
 

Or if I were to use the property declaration style instead, it seems just as confusing as the above try at this:

public static bool this int IsEven
{
    get { return this % 2 == 0; }
}
 

Note of course none of the above code samples actually work or compile.  Unfortunately, it would seem adding static extension methods would be just as clumsy as using this technique.  So, where does this leave C# for the future?  I certainly hope that they tackle this issue and I know they will at some point.  For now, it seems through Charlie Calvert’s blog, the focus for C# 4.0 seems to be around dynamic lookup and covariance/contravariance as described by Eric Lippert.  So, where extension everything falls onto the radar has yet to be determined.  That’s why it’s always important to let the C# team know what you want and don’t want as part of the releases when they solicit feedback.

Now that we covered that, let’s look at F#, which has extension everything, and see how it works. 


Extension Everything in F#

As I noted above, F# is a great general purpose programming language, rooted in functional programming, but supporting object oriented programming as well.  Extension everything is a good part of the story to state that F# is a more flexible language than C#.  The way that extensions are handled are rather elegant, but also such things as creating and publishing events are much more interesting in F#.  The latter part on events will be covered in a later post.


Extension Methods

As with C#, F# supports standard extension methods.  There are no requirements for them to be inside a static class, instead, it has a bit of a different syntax, which looks more like we’re extending a given type.  It reads much more clear as to what I’m doing than the C# solution.  To package up our extension methods, it’s best to package them inside a module, which is a way of gathering values, global state, type definitions and so on.  Then to reference these groups of functions and such, you simply use the open statement to reference the module.  Let’s look at a simple example of creating a ForEach method on the System.Collections.Generic.IEnumerable<‘a> class.

#light

namespace ExtensionFSharp

module CollectionExtensions =
  
  type System.Collections.Generic.IEnumerable with
    member this.ForEach(f:‘a -> unit) =
      for item in this do
        f item

 

Now with this, I’m able to create the ForEach method by just act like I’m creating the type.  Then I can specify the members I want to add to the given class.  Notice that I packaged this inside a namespace as well as a module, so I can reference it later.  In order to call this, I simply need to do the following:

#light

open System.Linq
open ExtensionFSharp.CollectionExtensions

let range = {1 .. 10} 
range.ForEach(fun i -> printfn "%i" i)

As you will notice from your Intellisense, the ForEach will show up just as all other extension methods that are declared for the IEnumerable<‘a> class.  But, what if we want to work with properties as well?  Well, that option is available to us as well.


Extension Properties

Declaring and using extension properties in F# is just as easy as it is to create an extension method as we did above.  Let’s take a simple example of creating a count property on IEnumerable<‘a>.  Yes, I realize that there is a method with that same name, but I want to approach it from a functional programming perspective while using tail recursion.  I covered recursion quite a bit in my series on getting back to basics on recursion.  Recursion is a very powerful construct in functional programming in order to avoid mutable state.  F# is optimized for the tail call, so that any tail recursive call should get optimized and therefore not run into any stack overflow issues.

#light 

namespace ExtensionFSharp 

module CollectionExtensions = 
   
  type System.Collections.Generic.IEnumerable with 
    member this.CountItems
      with get()
        let rec count_acc 
          (e:System.Collections.Generic.IEnumerator<‘a>)
          acc =
          if e.MoveNext() = false then
            acc
          else
            count_acc e (acc + 1)
        count_acc (this.GetEnumerator()) 0

What I was able to do is extend the IEnumerable<‘a> class with a property called CountItems with a get accessor.  From there, I create an inner accumulator function which takes the accumulator and the IEnumerator<‘a> used to iterate.  I have my break condition if there are no more, else I recursively tail call while incrementing the accumulator.  Simple and efficient to avoid mutable state.  In order to call this, is very simple much as above:

#light

open System.Linq
open ExtensionFSharp.CollectionExtensions

let range = {1 .. 100}
printfn "Contains %i items" range.CountItems

 

What this allows me to do is create a seq<int> which is the same as IEnumerable<int> while using the sequence comprehension syntax of 100 numbers.  From there, I am able to call the CountItems property on the range which will display 100.  What’s really nice is seeing this in action with my Intellisense as shown below:

fsharp_extension_property

Now that’s pretty slick!  And then what about static extension methods?  Well, it’s just as supported as anything else in F#.

 

Extension Static Methods

Declaring static extension methods is just about the same as the above ways of declaring, except for the inclusion of static and the removal of the instance name.  Using this style allows us to be pretty flexible with our designs.  Let’s look at a simple example of adding a sequence comprehension of characters to the Enumerable class. 

#light

namespace ExtensionFSharp

module CollectionExtensions =
        
  type System.Linq.Enumerable with  
    static member RangeChar(first:char, last:char) =
      {first .. last}

The implementation is pretty simplistic which behind the scenes calls the sequence comprehension of characters and returns it.  In order to call this, it’s also pretty simple as well.  Note, that I could have just used the {first .. last} instead of declaring this method in the first place, but it serves a good purpose as a demonstration.

#light

open System.Linq
open ExtensionFSharp.CollectionExtensions

let rangeChar = Enumerable.RangeChar(‘a’, ‘z’)
printfn "Contains %i items" rangeChar.CountItems

 

As you can see from this, there was really nothing to this.  Can we also work with events as well? 


Extension Events

Declaring and using events can be done, but I’m not really sure how and why I’d use it at the moment.  The problem lies in the fact that I cannot initialize these from the constructor as you can when you’re a first class citizen of the class.  To initialize anywhere else would put it into global state, which can be done, but not recommended.  I’m more than open to suggestion at this point.  In order to use this, it’s as easy as the following:

#light

namespace ExtensionFSharp

module CollectionExtensions =
  let event = new Event<System.EventArgs>()
  type System.Collections.Generic.List with
    member this.Added = event.Publish

 

As I’ve stated, it can be done, but I’m not sure of the need at the moment.  Let me know if I’m missing something glaring at this point.


What’s the Downside?

Are there any downsides?  Well, at the moment, when we create these extensions to our given classes in F#, they are only available to F#.  That means you cannot access these from C#, VB or any other language at the moment.  That cuts a little bit into the value proposition, but I think still very valuable to the language at this point.  And maybe in the future, they may address this, I can’t be completely sure.  I would definitely love to see it work though.


Wrapping it Up

This is meant to be just a glimpse into object oriented programming in F#.  In the next couple of posts, I’ll go more into the details and start from scratch.  The post I referenced in the beginning just made me think of this post that I had wanted to do for a while.  What I plan to cover next is the basics of interfaces, classes, structs and so on.  Then we can also go into deeper OOP topics and see where F# differs from C#.  As part of this discussion, I hope that it enforces that F# is indeed a great language for object oriented programming.

This entry was posted in F#, Functional Programming, OOP. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Sanny Jacobsson

    It would have been great to be able to do static extensions. For instance I would like to be able to statically extend the ThreadPool class with a QueueUserWorkItem method that takes generic parameters.

    ThreadPool.QueueUserWorkItem(WaitCallback callback, T state)

    Which would remove the need to cast the state object everytime.
    So instead of:
    ThreadPool.QueueUserWorkItem((s) => {
    MyClass mc = s as MyClass;
    if(mc != null){
    ….
    }
    }, state);

    one could write
    ThreadPool.QueueUserWorkItem((mc) => {

    }, state);

  • http://podwysocki.codebetter.com Matthew Podwysocki

    @hENRI

    Perhaps I had a poor choice there. How about adding parse methods to DateTime, or various other parsing methods to various value objects (Guid, Color, Point, etc). I don’t think that would add confusion at all. The Enumerable example was a point that it could be done. Adding Zip onto Enumerable is a great example as well which I should have done.

    Matt

  • hENRI

    What benefit do extension static methods have? I think extension instance methods are cool because save you from having to pass around a extra arguments. This makes a huge difference with functions that you chain (e.g. x.Where(…).Select(…).Sum(…) vs Enumerable.Sum( Enumerable.Select(Enumerable.Where(x, …),…),…)). Static extensions don’t have this benefit plus the indirection could also cause confusion.

    Is Enumerable.CharRange(‘a’, ‘z’) really better than EnumerableExtensions.CharRange(‘a’, ‘z’)?