Object Oriented F# - Encapsulation with Object Expressions

In the past, I've covered a bit about object oriented programming in F#.  I'd like to come back to that series as there is much yet to cover on this topic.  Last week, I spent some time with Erik Meijer at QCon and he and I both agreed that in some ways, F# is a better object oriented language than C# in some ways given some of the language flexibility.

Let's get caught up to where we are today:

 

Today's topic will be on object expressions and encapsulation in general.

 

Encapsulation

When we think of object oriented programming, encapsulation is a fundamental technique.  This is to hide design decisions that are likely to change behind well-defined boundaries, and thus protecting parts of the program from that change.  The benefits we get is that we can evolve our implementations over time without affecting other parts of our system.  It's a pretty elementary topic and yet deeply important. 

In F#, we have a couple of options we can talk about with regards to encapsulation which are object expressions and accessibility notations, the latter of which will be covered in a later post.

 

Object Expressions

One of the simplest ways of encapsulation in F# is to make them local to expressions or class definitions using the inner let bindings.  These values are not directly accessible from outside the scope.  Typically we use these to hold some sort of state which then is encapsulated inside implementation of our classes or expressions. 

Let's look at a simple implementation of a counter that encapsulates state.  We can encapsulate the state of the current count and then increment and have it keep count of our current state.  Let's look at how we might do that in F#.

#light
open Xunit

let counter =
  let count = ref 0
  fun () -> incr count; !count

[<Fact>]
let counter_should_increment()
  let c1 = counter()
  let c2 = counter()

  Assert.Equal(c1, 1)
  Assert.Equal(c2, 2)

You'll notice the use of the ref keyword which indicates that in order to properly be accessed and modified inside a closure, it cannot be mutable, but only a reference cell.  Brian McNamara of the F# team recently covered this on a blog post about the F# ref type and On lambdas, capture and mutability.  We also use the incr function which adds one to the reference cell.  What's important to realize here is that the count reference cell is never exposed to the outside world, only its value through the function.

Object expressions are another interesting part of F#.  This allows me to define local implementations of a given type and return it inline in a function, method or otherwise.  Let's look at a few examples.  The first one would be to create a comparer with two companies based upon size.

#light

type Company = { Name:string; Size:int; }

let companySizeComparer =
  { new System.Collections.Generic.IComparer<Company> with
      override x.Compare(c1, c2) =
        c1.Size.CompareTo(c2.Size)
  }
    
[<Fact>]
let companySizeComparer_ShouldCompareSizes() =
  let c1 = { Name = "Microsoft"; Size = 70000 }
  let c2 = { Name = "Google"; Size = 20000 }
    
  let c = companySizeComparer.Compare(c1, c2)
    
  Assert.Equal(c, 1)

Another example would be to create a Windows Form with an onload event prepopulated.  Note that is is a pretty naive example of how you would use it, but it shows how you might override items in a given class.

#light

open System.Windows.Forms

let helloform =
  { new Form() with
      member x.OnLoad(args)
        base.OnLoad(args)
        MessageBox.Show("Hello World!") |> ignore
  }

One last example I'll show is the simple map2 implementation from the F# libraries.  This gives me the ability to combine two collections as one, which should be identical to the new implementation of the Zip function in the new .NET 4.0 BCL.  I can create a simple IEnumerator<T> to encapsulate two collection iterators so that I can iterate over both at the same time and perform my calculation on each item.

let map2 f (e1 : IEnumerator<'a>) (e2 : IEnumerator<'b>)
  { new IEnumerator<'c> with 
        member x.Current = f e1.Current e2.Current
    interface IEnumerator with 
        member x.Current = box (f e1.Current e2.Current)
        member x.MoveNext()
            let n1 = e1.MoveNext()
            let n2 = e2.MoveNext()
            n1 && n2
        member x.Reset() = e1.Reset(); e2.Reset();
    interface System.IDisposable with 
        member x.Dispose() = e1.Dispose(); e2.Dispose()  }

To do something similar in C#, I'd have to go through the pomp and circumstance of having to create a full class, and then an implementation to then iterate over each, such as this:

public class SequenceMapEnumerator<T, U, V> : IEnumerator<V>
{
    private readonly IEnumerator<T> e1;
    private readonly IEnumerator<U> e2;
    private readonly Func<T, U, V> f;

    public SequenceMapEnumerator(
      IEnumerator<T> e1, 
      IEnumerator<U> e2, 
      Func<T, U, V> f)
    {
        this.e1 = e1;
        this.e2 = e2;
        this.f = f;
    }

    public V Current
    {
        get { return f(e1.Current, e2.Current); }
    }

    public void Dispose()
    {
        e1.Dispose();
        e2.Dispose();
    }

    object System.Collections.IEnumerator.Current
    {
        get { return f(e1.Current, e2.Current); }
    }

    public bool MoveNext()
    {
        var n1 = e1.MoveNext();
        var n2 = e2.MoveNext(); 
        return n1 && n2;
    }

    public void Reset()
    {
        e1.Reset();
        e2.Reset();
    }
}
 

And then the implementation of the map function might look like this:

public static IEnumerable<TResult> Map2<TArg1, TArg2, TResult>(
  this IEnumerable<TArg1> arg1, 
  IEnumerable<TArg2> arg2, 
  Func<TArg1, TArg2, TResult> func)
{
    var e1 = arg1.GetEnumerator();
    var e2 = arg2.GetEnumerator();
    var s = new SequenceMapEnumerator<TArg1, TArg2, TResult>
      (e1, e2, func);

    while (s.MoveNext())
        yield return s.Current;
}

Much like we did above, we could also encapsulate internal state inside of our object expression.  What we're allowing is just a simple session value holder for one item, and then implementing a counter using this technique.

type ISession<'a> =
  abstract member Get : unit -> 'a
  abstract member Put : 'a -> unit

let counter initialState =
  let state = ref initialState
  { new ISession<int> with
      member x.Put(value) =
        state := !state + value
      member x.Get() =
        !state
  }

So, as you can see, there's a bit to this and can make your code a bit more compact with local type definitions.  In some ways, with features like these, F# can be more object oriented than C#.

 

Conclusion

One of the more interesting object oriented features in F# is the object expression, and some associated techniques for encapsulation.  With the conciseness of the language, we are able to create local definitions of classes and interfaces to return from functions.  As the C# language evolves, we may start to see features such as this creep into the language much as other functional programming items have in the C# 5.0 timeframe.  But, I hold in my opinion that F# can be a better object oriented language, in part due to the fact that void is actually treated as a real type, which is a major problem in other languages such as C#.  With this, I can then really generalize my class implementations and not have to worry about what my return type may be, if any at all.

There's a lot more to cover in regards to object orientation in F#, including more of the back to basics approaches.



kick it on DotNetKicks.com

6 Comments

  • Your article is an interesting demonstration of object expressions and of F# conciseness, but it's a bit confusing at times.
    - I don't see how your article actually addresses the topic of encapsulation. Perhaps things would be clearer if you started by defining "encapsulation".
    - "One of the simplest ways of encapsulation in F# is to make them...": I don't understand what "them" refers to.
    - Your first example under "Object Expressions" does not illustrate object expressions, which is confusing.

  • Your C# Map2 function is far too complicated:

    public static IEnumerable Map2(
    this IEnumerable arg1,
    IEnumerable arg2,
    Func func)
    {
    using (var e1 = arg1.GetEnumerator())
    using (var e2 = arg2.GetEnumerator())
    {
    while (e1.MoveNext() && e2.MoveNext())
    {
    yield return func(e1.Current, e2.Current);
    }
    }
    }

  • @Richard

    True, it could be done that way, but I do like the conciseness of using the IEnumerator class implementation, just to show the same level of functionality.

    Matt

  • @Joh

    True, it was a little confusing as my example of encapsulation comes at the very end and not at the beginning and that's where the confusion lies.

    Matt

  • And how does that mean? I do not understand anything.

  • 一挙一動が勘違いに裏づけされた計算で行動してる感がある。

Comments have been disabled for this content.