Abstraction of Control Flow

One thing functional languages excel at is abstraction of control flow. Although you may not be familiar with the term, you’re probably already familiar with the concept. Programmers coming from an OO background are generally familiar with implementation polymorphism (overriding a method in a base class) and dependency injection (using an interface or base class to call methods an object, dynamically generated or otherwise); these are both forms of abstraction of control flow. They both allow the programmer to implicitly choose a different method to execute as long as that method conforms to some predefined signature. Let’s quickly remind ourselves what these too techniques look like.

Implementation Polymorphism

The classic example of implementation polymorphism is shapes that draw themselves. This technique is in just about every GUI library I know of and generally works very well in that scenario. The simplest example has a shape base class, with a draw method and then a couple of derived classes, say circle and square, that implement that method.

  abstract class Shape {

    public abstract void Draw();

  }

  class Circle : Shape {

    public int Diameter { get; set; }

    public override void Draw() {

      // ...     

    }

  }

  class Square : Shape {

    public int Size { get; set; }

    public override void Draw() {

      // ...     

    }

  }

You can then create a collection of type shape and draw all of the shapes. The draw method called depends on type of the shape, but we don’t care too much about that, we just call draw and on the base class and the right method is selected automatically, in other words the control flow has been abstracted:

  class Program {

    static void Main(string[] args) {

      List<Shape> shapes = new List<Shape>() {

        new Square() { Size = 3},

        new Circle() { Diameter = 2},

        new Square() { Size = 1}};

 

      foreach (var shape in shapes) {

        shape.Draw();

      }

    }

  }

Dependency Injection

I don’t know if dependency injection is the best name for this style of programming, I’ve also seen this style of programming referred to as the “hole in the middle pattern” – but I’m not sure I like that name either. When people talk about dependency injection they tend to focus on the aspect of dynamically creating objects via configuration, but for me this isn’t the important aspect. The aspect I like is the ability it gives you to inject code of your choosing into the middle of a method – to allow us to abstract control flow. Consider the following snippet in C#, here we define an interface IInt with a GeInt method. We now provide a library MyLib which has a TryGetInt method that accepts an IInt. A programmer using this library can now insert the code that they want to into the middle of TryGetInt by implementing the interface IInt.

  public interface IInt {

    int GetInt();

  }

 

  public class MyLib {

    static int TryGetInt(IInt i) {

      int res = -1;

      try {

        res = i.GetInt();

      }

      catch { }

      return res;

    }

  }

While this may not seem very much it allows us to achieve something that wasn’t possible in procedural/structured programming before object oriented programming came along. Modern object oriented programming languages, like C# for example, offer several ways to achieve this, you can use an interface, a base class or a delegate.

Most dependency intention advocates tend focus on the fact that you pass a dynamically generated object that implements IInt to TryGetInt and so can alter the applications behaviour by changing configuration files, and not recompiling the app, but for me this isn’t the big win. I like the fact that you now provide libraries that capture common programming patterns. Here we’re seen a very simple pattern being captured, we have a value being defaulted to -1 if an error occurs. The problem with this that that implementing an interface is a considerable amount of working, mean that it generally isn’t worth capturing small patterns like TryGetInt, your pattern needs to be quite big before you can justify putting it into a library.

Control Flow Abstraction in F# - Passing Functions as Values

While you can do both implementation polymorphism and dependency injection in F#, F# also offers another form abstraction of flow control that is a good deal simpler yet still very powerful – passing a function as parameter. Let’s take a quick look at a very simple case of this in F#:

#light

let apply f x = f x

 

apply (fun x -> x  + 1) 1

Here we define a function apply that takes another function f as a parameters and applies it to a value x. The thing to notice here is how much easier we’ve made it do what we were doing in our “Dependency injection” example. It’s now so simple to pass function around that its woth wrapping even the simplest patterns into a library function. And that’s exactly what functional programmers do. For example someone noticed that programmers spend a lot of time writing code like this:

let myNewSeq =

    { for x in { 1 .. 10 } -> x, x * x }

That is looping over lists to create another list, so they wrapped this into the library function “map”.

let map f l = { for x in l -> f x }

 

let myNewSeq =

    map (fun x -> x, x * x) { 1 .. 10 }

This act of capturing a pattern is something functional programmers love to do. The Seq module contains many more functions like map that operate on any IEnumerable list and capture common patterns of things you do to lists, like filter them and or enumerate them. While the map function itself may not save many characters of typing, compared with F#’s nifty list comprehension syntax, other functions really do, especially when combined together using F#’s pipe operator:

open System.IO

 

Directory.GetFiles(@"C:\users\robert\Documents", "*.txt")

|> Seq.map (fun x -> File.ReadAllLines(x)) // Read all lines from the file

|> Seq.filter (fun x -> x.Length > 3)      // Select all files with more than 3 lines

|> Seq.map (fun x -> x.[3])                // Select all the 3rd line

|> Seq.iter (printfn "%s")                 // Print the third line

Aside: Many functional programmers take a dim view of “design patterns” as they feel they are able capture patterns as functions there’s really no need for patterns. While I have some sympathy with this view, I feel it’s a bit extreme. It’s true many of the old design patterns have been replace with functions or language constructs but some design patterns are more abstract and can’t really just be replaced with mere function calls, I believe there is still much to be gained by studying design patterns.

But I find when I am writing F# it’s not just in libraries that I capture common patters. F# reduces the need to use copy and paste reuse when write code to virtually zero. Why? Because if you spot two bits of code that are similar you can also most always merge them and just pull out the differences into function. For example in C# it’s quite common to see code like this:

  class FileFunctions {

    internal void SaveFile() {

      SaveFileDialog fileDialog = new SaveFileDialog();

      fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

      if (fileDialog.ShowDialog() == DialogResult.OK) {

        File.AppendAllText(fileDialog.FileName, MyDocument.Data);

      }

    }

    internal void WriteFile() {

      OpenFileDialog fileDialog = new OpenFileDialog();

      fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

      if (fileDialog.ShowDialog() == DialogResult.OK) {

        MyDocument.Data = File.ReadAllText(fileDialog.FileName);

      }

    }

  }

It’s hard to refactor any further because the difference occurs in the middle of the function, but in F# this isn’t a problem:

let showDialogThen (dialog:#FileDialog) f =

    dialog.Filter <- "Text files (*.txt)|*.txt|All files (*.*)|*.*"

    if dialog.ShowDialog() = DialogResult.OK then

        f dialog.FileName

 

let saveFile() =

    showDialogThen (new SaveFileDialog())

        (fun x -> File.AppendAllText(x, MyDocument.Data))

 

let openFile() =

    showDialogThen (new OpenFileDialog())

        (fun x -> MyDocument.Data <- File.ReadAllText(x))

We’ve eliminated virtually all the repeated code! And it’s easy to see as the amount of configuration you apply to your file dialog increase so does the code saved, and if we need to update the configuration, for example change the filter, we’ve saved our self the task of having to make changes in multiple places. Okay it’s now easier to do this style of coding in C# 2.0 or 3.0 – but at least you know now where they stole their ideas from – functional programming.

Bookmark
dotnetkicks+, digg+, reddit+, del.icio.us+, dzone+, facebook+

Print | posted @ Tuesday, March 04, 2008 10:11 PM

Comments on this entry:

No comments posted yet.

Your comment:

(Note: all comments are moderated so it may take sometime to appear)

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 6 and 1 and type the answer here: